TypeScrip通过调用this call signature,在一定程度上支持使用the array filter()
method到narrow的结果数组类型:
interface ReadonlyArray<T> {
filter<S extends T>(
predicate: (value: T, index: number, array: readonly T[]) => value is S,
thisArg?: any
): S[];
}
这意味着predicate
参数需要是返回类型为type predicate的custom type guard function.
不幸的是,类型脚本不支持infer个来自函数实现的类型谓词,即使在像`x=>;typeof x="字符串"这样的简单情况下也是如此.在microsoft/TypeScript#38390岁的时候,有一个悬而未决的问题需要这样的支持.
即使发生了这种情况,它也可能不适用于({values})=>values.includes("a")
;即使直接拨打the includes()
array method也不能起到打字保护的作用.这个请求在microsoft/TypeScript#31018中被拒绝了,因为它太复杂了,无法正确处理.
这意味着您必须使用显式编写为定制类型保护函数的回调来调用filter()
,并明智地使用它.以下是一种生成类型保护函数的可能方法,该方法适用于比您已有的情况稍微更一般的情况:
const objPropHasArrayContainingStringLiteral =
<K extends string, T extends string>(
propName: K,
stringLiteral: T
) => <U extends Record<K, readonly string[]>>(
obj: U
): obj is (
U extends Record<K, readonly (infer V extends string)[]> ?
[T] extends [V] ? U : never : never
) => obj[propName].includes(stringLiteral);
这里,objPropHasArrayContainingStringLiteral(propName, stringLiteral)
产生类型保护函数,该函数在propName
处判断对象是否具有包含stringLiteral
的数组属性.
类型输出是使用distributive conditional type将union type U
筛选到其在键K
处的属性是包含T
个元素的只读数组的那些成员(至少,假设T
是字符串literal type,并且U
的数组类型本身持有字符串文字类型).如上所述,要做到正确是很棘手的.
因此,对于您的示例,它将是objPropHasArrayContainingStringLiteral("values", "a")
:
const objsWithA = foo.filter(objPropHasArrayContainingStringLiteral("values", "a"));
/* const objsWithA: ({
readonly id: "t1";
readonly values: readonly ["a", "b"];
} | {
readonly id: "t2";
readonly values: readonly ["a", "c"];
})[] */
这很管用,万岁.但我不确定我们在这里解决的问题是否值得采取更普遍的解决方案.如果你只打算这样做一次,那么你不妨只做内联:
const objsWithA = foo.filter(<U extends { values: readonly string[] }>(
obj: U): obj is U extends { values: readonly (infer V extends string)[] } ?
"a" extends V ? U : never : never => obj.values.includes("a")
);
这是一样的,只是K
硬编码为"values"
,T
硬编码为"a"
.本质上,它是说回调将U
并集过滤到其values
属性为只读数组的那些成员,该数组可能包含类型"a"
的值.
请注意,编译器实际上并不验证定制类型保护函数的实现.您可以将obj.values.includes("a")
更改为!obj.values.includes("a")
或obj.values.includes("whoops")
,编译器不会注意到.所以,再说一次,你必须小心.
Playground link to code个