我正在努力缩小一个复杂的受歧视联盟的范围。
我使用了一堆
if
并提前返回来消除所有情况,但最后,我发现自己有 2 个默认类型位于鉴别器上,是联合类型,并且缩小停止工作。
我有一个简单的片段来演示我的问题。
interface Foo {
foo: string;
type: 'foo';
}
interface BarBaz {
bar: string;
type: 'bar' | 'baz';
}
const func = function (arg: Foo | BarBaz) {
if (arg.type === 'bar' || arg.type === 'baz') {
return 'whatever';
}
arg; // type Foo or BarBaz expecting Foo
};
我想,我应该只有唯一的鉴别器,但是有没有一种简单的方法可以摆脱这个死胡同?
这被认为是 TypeScript 的设计限制,报告于 microsoft/TypeScript#31404。虽然检查的真实分支按预期缩小,但 TypeScript 缺乏在错误分支中执行任何有用操作所需的反事实分析。
如果你需要这个工作,你就必须解决它。一种方法是创建一个自定义类型保护函数来显式区分联合:
function discrimUnion<
T extends Record<K, any>,
K extends PropertyKey,
const V extends T[K]
>(t: T, k: K, ...v: V[]): t is Extract<T, Record<K, V>> {
return v.includes(t[k]);
}
const func = function (arg: Foo | BarBaz) {
if (discrimUnion(arg, "type", "bar", "baz")) {
arg // BarBaz
return 'whatever';
}
arg; // Foo
return;
};
switch
/case
语句,这似乎更能确定详尽性:
const func = function (arg: Foo | BarBaz) {
switch (arg.type) {
case "bar":
case "baz": {
arg; // BarBaz
return 'whatever'
}
default: {
arg // Foo
return;
}
}
}