首页 > 解决方案 > 使用 Function.prototype.bind 时类型谓词丢失

问题描述

TS 3.2.4 对我来说这个错误:

const isArray: typeof Array.isArray = Array.isArray.bind(Array);

错误是

error TS2322: Type '(arg: any) => boolean' is not assignable to type '(arg: any) => arg is any[]'.
  Signature '(arg: any): boolean' must be a type predicate.

似乎正在发生的事情是 Array.isArray 的类型谓词在使用 .bind 时丢失了。我测试了几个类型谓词函数,并且总是得到相同的错误。

这是一个真正的 TS 错误,还是我做错了什么?

标签: typescripttypes

解决方案


与其说是错误,不如说是设计限制。Typescript 的做法是使用不同数量的参数对其strictBindCallApply进行多次重载:bind

bind<T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T): (...args: A) => R;
bind<T, A0, A extends any[], R>(this: (this: T, arg0: A0, ...args: A) => R, thisArg: T, arg0: A0): (...args: A) => R;
bind<T, A0, A1, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1): (...args: A) => R;
bind<T, A0, A1, A2, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2): (...args: A) => R;
bind<T, A0, A1, A2, A3, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3): (...args: A) => R;
bind<T, AX, R>(this: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R;

这种方法的问题是类型保护函数返回一个简单的boolean,它将被捕获R并转发给返回类型。但是守卫行为没有被捕获或转发R。它只是被删除了。

虽然可以编写bind考虑到类型保护行为的函数,但很难获得适用于所有可能排列的版本。

例如,这适用于您的情况:

declare function bind<T, A0, A extends any[], R>(fn: (this: T, arg0: A0 | R, ...args: A) => arg0 is R, thisArg: T): (arg0: A0 | R, ...args: A) => arg0 is R;
const isArray: typeof Array.isArray = bind(Array.isArray, Array);

但这假设受保护的参数是第一个参数,没有什么可以阻止您使用 10 个参数来保护最后一个参数。要捕获这种情况,您需要有一个重载arg0...arg9(以及介于两者之间的所有重载)。

我将搜索一个问题,但我的猜测是类型系统无法以有效的方式对此进行建模,并且对于这个角落用例来说,使用类型保护版本来拥挤重载是不值得的。

编辑在 GitHub 上找不到错误,您可以发布一个以获得对此事的官方意见,而不仅仅是一个关于 SO 意见的人 :)


推荐阅读