我对下面的问题感到困惑。我有一个简单的标签联盟:
type MyUnion =
| { tag: "Foo"; field: string; }
| { tag: "Bar"; }
| null;现在我有了一个通用函数:
const foo = <T extends MyUnion>(value: T) => {
// Narrowing works as expected.
if (value === null) { return; }
const notNull: NonNullable<T> = value;
// Narrowing works with field access, but not with `Extract` type.
if (value.tag !== "Foo") { return; }
const s: string = value.field; // Field can be accessed -> all good here.
const extracted: Extract<T, { tag: "Foo" }> = value; // error!
};(游乐场)
最后一行将导致此错误:
Type 'T & {}' is not assignable to type 'Extract<T, { tag: "Foo"; }>'.我真的不明白为什么。将其写入函数之外,而不使用泛型作为Extract<MyUnion, { tag: "Foo" }>,则会得到完全正确的类型。这里发生了什么事?
注意:在我的实际代码中,函数有另一个参数回调:(v: Extract) => void。调用该回调会导致相同类型的错误。因此,我的目标是以某种方式实现这个目标:游乐场。因此,函数foo基本上对一个值执行特定的检查,以某种方式处理特殊情况,然后用净化后的值调用回调。我假设TypeScript允许我表达这种抽象?
发布于 2022-11-21 10:35:14
这可以看作是TypeScript中的一个设计限制。这个问题是Extract是一个条件类型,它依赖于泛型类型T。
在使用条件的代码中,T几乎没有被指定。当然,T有一个约束,强制它成为MyUnion的一个子类型。但是还不知道具体类型,因为它是由函数的调用方指定的。每当编译器遇到这样的条件类型时,它就无法计算它。类型本质上保持不透明,直到只有类型Extract<T, { tag: "Foo" }>的值才能安全地分配给它。
您可以在#33484或#28884中找到对类似问题的讨论。有很多关于泛型和条件的情况,在这种情况下,类型对读代码的人来说可能是有声的,但是编译器由于尚未解决的类型而停止推理。
你可能会想知道为什么这样做是可行的:
const notNull: NonNullable<T> = value;#49119将NonNullable从条件类型更改为只与T和{}相交的条件类型。
- type NonNullable<T> = T extends null | undefined ? never : T;
+ type NonNullable<T> = T & {};同样的PR还引入了对控制流分析的改进。如果检查泛型类型的变量是否不是null,则会将变量的类型与{}相交。
这使得可分配性成为可能,因为NonNullable<T>和value计算值的类型都是T & {}的。
https://stackoverflow.com/questions/74517152
复制相似问题