TypeScript 条件类型的映射与受约束的泛型不同吗?

Posted

技术标签:

【中文标题】TypeScript 条件类型的映射与受约束的泛型不同吗?【英文标题】:Do TypeScript conditional types map differently than constrained generics? 【发布时间】:2021-02-07 05:15:35 【问题描述】:

我无法理解为什么下面的代码会这样运行:

type MapOverString<T extends string> =  [K in T]: K ;

type IfStringMapOverIt<T> = T extends string ? MapOverString<T> : never;

type ThisWorks = MapOverString<'a'>;
//  a: 'a' 

type ThisAlsoWorks = IfStringMapOverIt<'a'>;
//  a: 'a' 

type Union = 'a' | 'b' | 'c';

type ThisWorksToo = MapOverString<Union>;
//  a: 'a', b: 'b', c: 'c' 

type ThisDoesnt = IfStringMapOverIt<Union>;
// MapOverString<'a'> | MapOverString<'b'> | MapOverString<'c'>

Playground link

我一定是遗漏了一些东西,因为MapOverStringIfStringMapOverIt 似乎它们的功能应该相同。

最终,我使用字符串文字和泛型通过配置类型的排列进行级联。例如,如果您希望 StringConfig&lt;T&gt; 配置有选项 'a' | 'b' | 'c'

type ConfigMap<T> = T extends number
  ? NumberConfig
  : T extends string
    ? StringConfig<T>
    : never

type MyConfig = ConfigMap<'a' | 'b' | 'c'> // so many sad faces

有人可以启发我吗?这是怎么回事?

【问题讨论】:

这真的很有趣。标题的答案似乎是(来自您的代码):是的,他们确实如此。有两种选择实际上很酷。请注意联合中是否包含非字符串 ("a" | 42 | "b") 的区别。不过,我不知道 为什么 或规则是什么。 :-) 它背后的逻辑似乎很合乎逻辑,就像如果你有一个类型 type SeparateStrings&lt;T&gt; = T extends string ? T : never 并且你像 SeparateStrings&lt;'a' | 42 | 'b'&gt; 一样使用它并期望你得到 'a' | 'b',它的作用是合乎逻辑的:它迭代联合类型,而不是将此联合作为单一类型使用。所以这样的行为似乎是合理的,但不知道如何让它做你想做的事,这真的很有趣:p 【参考方案1】:

这是条件类型的分布属性的应用。裸类型参数的条件将触发此行为,T extends string 满足此条件。您可能还会看到 T extend TT extends anyT extends unknown 用于这个原因,只是为了触发分发。

您可以在handbook 中阅读有关分布式条件类型的更多信息

您可以通过在元组[T] extends [string] 上使用条件来禁用分发。这样做的效果类似于正则条件,只是由于类型参数不再是裸分布才会显示出来。

type StringConfig<T extends string> =  [K in T]: K ;
type NumberConfig =

type ConfigMap<T> = [T] extends [number]
  ? NumberConfig
  : [T] extends [string]
    ? StringConfig<T>
    : never

export type MyConfig = ConfigMap<'a' | 'b' | 'c'> // so many sad faces
let x:MyConfig = 
  a:'a',
  b:'b',
  c: 'c'

Playground Link

【讨论】:

这正是我所缺少的,谢谢!文档中引用的示例也有助于阐明设计决策。欣赏周围的工作,以及 - 对我来说非常完美:)

以上是关于TypeScript 条件类型的映射与受约束的泛型不同吗?的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript 中每个键的泛型映射对象类型

Typescript 推断的泛型类型在条件类型中是未知的

如何使用带有 Typescript 的泛型将中继(graphql)连接映射到边缘节点

扩展类型的泛型和 Typescript 中的普通类型有啥区别?

TypeScript 中具有泛型类型参数的泛型类型的替代方案

前端进阶-TypeScript高级类型 | 泛型约束泛型接口泛型工具类型