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
我一定是遗漏了一些东西,因为MapOverString
和IfStringMapOverIt
似乎它们的功能应该相同。
最终,我使用字符串文字和泛型通过配置类型的排列进行级联。例如,如果您希望 StringConfig<T>
配置有选项 '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<T> = T extends string ? T : never
并且你像 SeparateStrings<'a' | 42 | 'b'>
一样使用它并期望你得到 'a' | 'b'
,它的作用是合乎逻辑的:它迭代联合类型,而不是将此联合作为单一类型使用。所以这样的行为似乎是合理的,但不知道如何让它做你想做的事,这真的很有趣:p
【参考方案1】:
这是条件类型的分布属性的应用。裸类型参数的条件将触发此行为,T extends string
满足此条件。您可能还会看到 T extend T
或 T extends any
或 T 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 的泛型将中继(graphql)连接映射到边缘节点
扩展类型的泛型和 Typescript 中的普通类型有啥区别?