如何解决打字稿对 Array.includes() 过于严格的问题
什么是修复以下代码且对可读性影响最小的干净方法?
操场
type Fruit = "apple" | "orange";
type Food = Fruit | "potato";
const fruits: Fruit[] = ["apple", "orange"];
function isFruit(thing: Food) {
return fruits.includes(thing); // "potato" is not assignable to type 'Fruit'.
}
回答
首先,请阅读 TypeScript 3.x 天的 QA,其中有人问了与您基本相同的问题:TypeScript const assertions: how to use Array.prototype.includes?
现在,在您的情况下,作为@CertainPerformance 建议unknown[](丢失类型信息)的替代方案,您可以合法地扩大fruits到readonly string[](不使用as),它与Fruit和兼容Food:
type Fruit = "apple" | "orange";
type Food = Fruit | "potato" | "egg";
const fruits: readonly Fruit[] = ["apple", "orange"];
function isFruit(food: Food): food is Fruit {
const fruitsAsStrings: readonly string[] = fruits;
return fruitsAsStrings.includes(food);
}
另一种(理论上更“正确”)的方法是向接口添加一个变体 includes成员ReadonlyArray<T>(如链接的 QA 中所建议的那样),它允许U成为 ofT而不是相反的超类型。
interface ReadonlyArray<T> {
includes<U>(x: U & ((T & U) extends never ? never : unknown)): boolean;
}
type Fruit = "apple" | "orange";
type Food = Fruit | "potato" | "egg";
const fruits: readonly Fruit[] = ["apple", "orange"];
function isFruit(food: Food): food is Fruit {
return fruits.includes(food);
}
说了这么多......如果你打算使用集合类型作为值/类型集成员资格测试,你应该使用 JavaScriptobject而不是数组:不仅是因为性能原因(因为object键查找是O(1)数组includesis O(n),但也因为 TypeScript 对keyof类型更有效。
...实现这是读者的练习。