首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >类型缩窄和“`Extract`”助手与泛型交互中的意外类型错误

类型缩窄和“`Extract`”助手与泛型交互中的意外类型错误
EN

Stack Overflow用户
提问于 2022-11-21 10:01:17
回答 1查看 44关注 0票数 2

我对下面的问题感到困惑。我有一个简单的标签联盟:

代码语言:javascript
复制
type MyUnion = 
    | { tag: "Foo"; field: string; } 
    | { tag: "Bar"; } 
    | null;

现在我有了一个通用函数:

代码语言:javascript
复制
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!
};

(游乐场)

最后一行将导致此错误:

代码语言:javascript
复制
Type 'T & {}' is not assignable to type 'Extract<T, { tag: "Foo"; }>'.

我真的不明白为什么。将其写入函数之外,而不使用泛型作为Extract<MyUnion, { tag: "Foo" }>,则会得到完全正确的类型。这里发生了什么事?

注意:在我的实际代码中,函数有另一个参数回调:(v: Extract) => void。调用该回调会导致相同类型的错误。因此,我的目标是以某种方式实现这个目标:游乐场。因此,函数foo基本上对一个值执行特定的检查,以某种方式处理特殊情况,然后用净化后的值调用回调。我假设TypeScript允许我表达这种抽象?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-11-21 10:35:14

这可以看作是TypeScript中的一个设计限制。这个问题是Extract是一个条件类型,它依赖于泛型类型T

在使用条件的代码中,T几乎没有被指定。当然,T有一个约束,强制它成为MyUnion的一个子类型。但是还不知道具体类型,因为它是由函数的调用方指定的。每当编译器遇到这样的条件类型时,它就无法计算它。类型本质上保持不透明,直到只有类型Extract<T, { tag: "Foo" }>的值才能安全地分配给它。

您可以在#33484#28884中找到对类似问题的讨论。有很多关于泛型和条件的情况,在这种情况下,类型对读代码的人来说可能是有声的,但是编译器由于尚未解决的类型而停止推理。

你可能会想知道为什么这样做是可行的:

代码语言:javascript
复制
const notNull: NonNullable<T> = value;

#49119NonNullable从条件类型更改为只与T{}相交的条件类型。

代码语言:javascript
复制
- type NonNullable<T> = T extends null | undefined ? never : T;
+ type NonNullable<T> = T & {};

同样的PR还引入了对控制流分析的改进。如果检查泛型类型的变量是否不是null,则会将变量的类型与{}相交。

这使得可分配性成为可能,因为NonNullable<T>value计算值的类型都是T & {}的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74517152

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档