typescript - 精炼元组的联合类型
我正在尝试优化一种类型,它要么是这两个元组中的一个,但我无法让 TypeScript 配合。
declare const foo: [string, string] | [number, number];
declare function isNumber(x: unknown): x is number;
if (isNumber(foo[0])) {
foo; // not refined
}
这可能吗?ts playground
答案#1
在 microsoft/TypeScript#42384 上有一个功能请求,希望在缩小属性类型时也能缩小父对象类型。目前,TypeScript 仅在检查其判别属性时过滤判别联合类型。但是,虽然类型 [number, number] | [string, string] 是一个联合类型,它并不被视为 判别联合类型,因为 number 和 string 都不是有效的 判别值。判别属性的类型需要具有字面量类型,例如 "stringLiteral" 或 123。number 和 string 类型太广泛了。
在 microsoft/TypeScript#42384 实现之前,你需要找到解决方法。如果你愿意,你可以编写自己的自定义类型保护函数,该函数接受一个联合类型的对象、一个键名,以及该键处属性的类型保护,并使用它们来区分联合类型:
// discriminate union T to members with a value of type V at key K
function hasPropOfType<T, K extends keyof T, V extends T[K]>(
obj: T, k: K, guard: (x: T[K]) => x is V): obj is Extract<T, Record<K, V>> {
return guard(obj[k])
}
然后你可以写 hasPropOfType(foo, 0, isNumber) 来代替 isNumber(foo[0]):
if (hasPropOfType(foo, 0, isNumber)) {
foo;
//^? const foo: [number, number]
foo[1].toFixed()
} else {
foo;
//^? const foo: [string, string]
foo[1].toUpperCase()
}
代码的 Playground 链接
答案#2
TS 不会跟踪元组成员之间的相关性,我建议使用一些通用方法:
操场
type DoubleTuple<T> = [T, T];
declare const foo: DoubleTuple<number> | DoubleTuple<string>;
type Types = {string: string, number: number};
declare function isDoubleTupleOfType<T extends keyof Types>(x: DoubleTuple<any>, type: T): x is DoubleTuple<Types[T]>;
if (isDoubleTupleOfType(foo, 'number')) {
foo;
//^?
const n = foo[0];
// ^?
}
