TypeScript 索引访问类型约束行为异常

标签 typescript generics type-constraints discriminated-union

举个例子

interface Person<TName> {
    name: TName;
}

type People =
    | Person<"Levi">
    | Person<"Julien">

type FilteredPersonBase<TPerson extends People> = TPerson["name"] extends "Levi" ? TPerson : never;
type FilteredPerson = FilteredPersonBase<People>; // never

type FilteredPersonBase2<TPerson extends People> = TPerson extends { name: "Levi" } ? TPerson : never;
type FilteredPerson2 = FilteredPersonBase2<People>; // Person<"Levi">

我希望这两个 FilteredPersonFilteredPerson2解决Person<"Levi"> ,但他们没有。为什么在类型约束中使用索引访问运算符的解析方式与使用内联类型不同?

最佳答案

我同意第二个应该解决人 Person<"Levi"> ,但让我们看看这是如何发生的:

FilteredPersonBase2<People>
    // FilteredPersonBase2 is a distributive conditional type since
    // TPerson appears nakedly in the condition of the conditional type
    => FilteredPersonBase2<Person<"Levi">> | FilteredPersonBase2<Person<"Julien">>
    // The conditional type is applied to each member of the union
    => (Person<"Levi"> extends { name: "Levi" } ? Person<"Levi"> : never) |  (Person<"Julien"> extends { name: "Levi" } ? Person<"Julien"> : never)
    // The first application return Person<"Levi">, the second resolves to never
    => Person<"Levi"> | never
    // Never is removed in a union 
    => Person<"Levi">

这样做的原因是 distributive behavior of conditional types

但是条件类型只分布在裸类型参数上。在第一个示例中,条件结束 TPerson["name"]这不是裸类型参数,因此不会发生分配。展开我们得到的条件类型:

FilteredPersonBase<People> =>
    // No distribution 
    People["name"] extends "Levi" ? People : never => 
    // People["name"] just resolves to the union
    "Levi" | "Julien" extends "Levi" ? People : never => 
    // The union does not extend a constituent of the union
    never 

由于没有发生分发,TPerson["name"]只是解析为 "Levi" | "Julien"联合不会扩展成分。反之亦然 ("Levi" extends TPerson["name"]),但这只会解析为联合 (Person),因为联合的成分是联合的子类型。

只是为了好玩,您可以强制分发 TPerson使用始终为真的条件,如 TPerson extends TPerson :

type FilteredPersonBase<TPerson extends People> = TPerson extends TPerson ? TPerson["name"] extends "Levi" ? TPerson : never : never;
type FilteredPerson = FilteredPersonBase<People>;  // Person<"Levi">

关于TypeScript 索引访问类型约束行为异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57040532/

相关文章:

node.js - node_modules 从 "main"(package.json) 中指定的目录导入?

typescript - 带有 typescript 的 winston 自定义传输

javascript - 如何根据 Angular 选择值隐藏输入字段?

typescript - Webpack 尝试将 .ts 文件解析为 .js,即使 .ts 在 resolve.extensions 列表中排在第一位

c# - 转换为具有接口(interface)的通用类

c# - Xaml 资源中的通用类型

c# - 为类型 T 编写扩展方法;如何为 T 的字段添加类型约束?

c# - 如何创建可以返回 double 或小数的通用 C# 方法?

scala - 如何使用 None 类型约束专门化一个特征?

swift - 如果 Swift 协议(protocol)是用类型约束定义的,为什么不能直接访问该类型的属性/方法?