我认为我已经找到了定义谓词的最佳方法:
declare function isNumber<T>(x: T): x is Extract<T, number>;
declare function isFunction<T>(x: T): x is Extract<T, Function>;
... and so on
这种方法在用于过滤数组时会产生很好的缩小类型,例如:
type Handler = () => void;
declare const a: (number|string)[];
declare const b: string[];
declare const c: (Handler|null)[];
const a1 = a.filter(isNumber); // number[] 👍
const b1 = b.filter(isNumber); // never[] 👍
const c1 = c.filter(isFunction); // Handler[] 👍
不幸的是,
unknown
和any
会导致令人惊讶的行为:
declare const d: any[];
declare const e: unknown[];
const d1 = d.filter(isNumber); // any[] 😩 want number[]
const e1 = e.filter(isNumber); // never[] 😩 want number[]
所以这毕竟不是最好的方法!然而,即使是 Typescript 手册中定义谓词的方法在用于过滤时也会表现得“奇怪”:
declare function isNumber2(x: any): x is number;
declare function isFunction2(x: any): x is Function;
const a2 = a.filter(isNumber2); // number[] 👍
const b2 = b.filter(isNumber2); // string[] 🤯 want never[]
const c2 = c.filter(isFunction2); // (Handler|null)[] 🤯 want Handler[]
const d2 = d.filter(isNumber2); // number[] 👍
const e2 = e.filter(isNumber2); // number[] 👍
我花了很长时间尝试各种方法,例如重载、通用参数约束等,以使其适用于所有上述情况,但一无所获。有没有一种方法可以定义谓词,在过滤时可以很好地缩小数组范围? (抱歉,我知道“很好”是主观的。通常它是最狭窄的预期类型。)或者我只需要选择一种可以容忍的方法?还在寻找指导,因为我目前正在学习 Typescript,所以我完全走在错误的道路上。
我认为你可以使用泛型和条件类型断言来实现你想要的。
declare function isNumber<T>(value: T): value is T extends number ? T : never;
declare function isFunction<T>(value: T): value is T extends Function ? T : never;
上面给出了我在每个示例中所期望的输出。