JKi*_*ian 5 types discriminated-union typescript typescript-generics
我们的代码库中有一个实用程序可以生成类型保护来缩小可区分联合的范围:
export type ExtractBranchFromUnion<
UNION,
DISCRIMINANT extends keyof UNION,
BRANCH extends UNION[DISCRIMINANT],
> = UNION extends Record<DISCRIMINANT, BRANCH> ? UNION : never;
export function narrow<
UNION,
DISCRIMINANT extends keyof UNION,
BRANCH extends UNION[DISCRIMINANT],
>(
discriminate: DISCRIMINANT,
branch: BRANCH,
): (
item: UNION,
) => item is ExtractBranchFromUnion<UNION, DISCRIMINANT, BRANCH> {
return (item): item is ExtractBranchFromUnion<UNION, DISCRIMINANT, BRANCH> =>
item[discriminate] === branch;
}
Run Code Online (Sandbox Code Playgroud)
它可以很容易地在调用中使用,filter以将数组缩小到特定的联合成员,但是它要求您记住as const在字符串文字branch参数之后添加,否则它会错误地推断类型并给出太宽的结果:
type A = {type: 'A'; a: string};
type B = {type: 'B'; b: string};
type SimpleUnion = A | B;
const arr: SimpleUnion[] = [];
// BAD: type is SimpleUnion[]
const badListOfBs = arr.filter(narrow('type', 'B'));
// GOOD: type is B[]
const goodListOfBs = arr.filter(narrow('type', 'B' as const));
Run Code Online (Sandbox Code Playgroud)
虽然这个问题可以通过预制常量得到部分缓解,
const Types = {
'A': 'A',
'B': 'B',
} as const;
// OKAY: type is B[] but it requires a premade constant
const okayListOfBs = arr.filter(narrow('type', Types.B));
Run Code Online (Sandbox Code Playgroud)
它仍然不能防止人们忘记as const字面值并困惑为什么事情不起作用(以及as const上面的示例两个代码块中看起来丑陋的事实)。
有没有办法让 TS 在提供narrow如上所示的字符串文字时自动推断出较窄的类型?或者至少有一种方法可以显示一条漂亮的错误消息?
您只需要向Branch泛型添加额外的限制:
export type ExtractBranchFromUnion<
Union,
Discriminant extends keyof Union,
Branch extends Union[Discriminant],
> = Union extends Record<Discriminant, Branch> ? Union : never;
export function narrow<
Union,
Discriminant extends keyof Union,
Branch extends Union[Discriminant] & PropertyKey, // <------ change is here
>(
discriminate: Discriminant,
Branch: Branch,
): (
item: Union,
) => item is ExtractBranchFromUnion<Union, Discriminant, Branch> {
return (item): item is ExtractBranchFromUnion<Union, Discriminant, Branch> =>
item[discriminate] === Branch;
}
type A = { type: 'A'; a: string };
type B = { type: 'B'; b: string };
type SimpleUnion = A | B;
const arr: SimpleUnion[] = [];
const badListOfBs = arr.filter(narrow('type', 'B')); // B[]
const goodListOfBs = arr.filter(narrow('type', 'A')); // A[]
Run Code Online (Sandbox Code Playgroud)
我添加了这个:Branch extends Union[Discriminant] & PropertyKey。
我愿意打赌鉴别器值应该是string | number | symbol
您可能已经注意到,我将所有通用名称大写,因为我发现它很难阅读。但这是偏好问题。
| 归档时间: |
|
| 查看次数: |
416 次 |
| 最近记录: |