TypeScript type for Function.prototype.bind wrongly preserves fields on a function

ql3eal8s  于 23天前  发布在  TypeScript
关注(0)|答案(9)|浏览(24)

当在函数上调用 bind 时,JavaScript不会将添加到该函数的属性复制过去,然而 TypeScript 将它们视为已复制。

TypeScript 版本: 3.7-Beta
搜索词: function bind
代码

const a = () => { }
a.hello = 'hi'

const b = a.bind({})

b.hello.split("") // Uncaught TypeError: Cannot read property 'split' of undefined

预期行为:hello 字段不应该出现在 typeof b 上。
实际行为:hello 被认为是存在于 typeof b 上的,导致程序崩溃。
** playground 链接:**http://www.typescriptlang.org/play/?ts=3.7-Beta&ssl=1&ssc=1&pln=7&pc=1#code/MYewdgzgLgBAhjAvDAFASiQPhgbxgXwCg4A6ACwFMAbKkJGAcjIEsHDDRJYAje075mAAmKHPjTtu5arRIQADlWZQUAIlUYA9JpgBVMMDgBXAOZlYAFQCe8igFEATg5AOAXDADCcMGBCwHFHBCMPLOtg5QVowKSlAMMCAAZjBGwhSJghRChEA
相关问题:#212 似乎讨论并最终实现了这个问题,尽管它与此无关。

6ss1mwsb

6ss1mwsb1#

BTW, this only happens when using this specific overload.

const f = (a: boolean) => a;
f.value = 42;

const g = f.bind(undefined);
g.value; // OK!

const h = f.bind(undefined, true);
h.value; // Error, as expected
2wnc66cl

2wnc66cl2#

当然不是正确的行为,但我们需要一种方式来表达"仅调用签名",这不能用当前类型系统原语表达。

ao218c7q

ao218c7q3#

But it really looks like it's just a matter of rewriting the first overload.

bind<T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T): (...args: A) => R;
jhkqcmku

jhkqcmku4#

bind 今天保留了重载,而提议的形式不会:

declare function fn(s: string): number;
declare function fn(n: number): string;

const b = fn.bind({});
// Both OK, but argument capture can't preserve overloads
b(10).toLowerCase();
b("").toFixed();
7fhtutme

7fhtutme5#

我实际上在意识到它破坏了重载测试之前就做出了这个改变,而且我找不到一种保留它们的方法,我想这就是你在#34338(评论)中的观点。
很好的测试覆盖率 👌

omhiaaxx

omhiaaxx6#

经过进一步思考,通过与排除非Function键的Map类型相交,可能产生never

然而,需要有人在实践中遇到这个问题来证明增加这种额外复杂性是合理的。这似乎是一个相当困难的错误。这是在实际出现的bug中遇到的,还是只是你注意到的问题?

r1zk6ea1

r1zk6ea17#

不,实际上我遇到了这个问题。我有一个API,允许你使用函数上的字段“部分应用”一个函数,这将返回一个具有相同接口的函数,直到最终被调用。我还需要克隆这个函数,绑定似乎是最简单的方法,但是尽管类型说明它们是,字段并没有被添加。我也尝试了Map类型,但无法使其正常工作,因为类型系统似乎认为当你这样做时,也没有可调用的签名留下(我也记得在做这件事时遇到过泛型测试的问题)。话虽如此,我可能在实现中遗漏了一些东西。

daupos2t

daupos2t8#

type RemoveProps<T> = { [K in keyof T]: never };
type SafeBind<T> = T & RemoveProps<T>;

interface Function {
  bind2<T>(this: T, arg: any): SafeBind<T>;
}

function f(n: number): string;
function f(n: string): number;
function f(n: any) {
  return n;
}
f.prop = 0;

const t = f.bind2(null);
t(0);
t("");
t.prop; // never
qeeaahzv

qeeaahzv9#

T & 是我所缺少的,看起来它正是所需要的。👍

相关问题