0Pricing
TypeScript Academy · 课时

可分配条件类型

控制条件类型如何分布到联合类型上。

可分配条件类型 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。

一种令人意外的行为

条件类型有一条特殊规则:当被检查的类型是裸类型参数,并且传入的是联合类型时,条件类型会分别对每个成员进行分布,然后将结果合并为联合类型。

这是 Exclude、Extract 以及许多工具类型的基础。

type ToArray<T> = T extends unknown ? T[] : never;

type A = ToArray<string | number>;
// string[] | number[]  (NOT (string | number)[])

“裸”是什么意思

“裸”表示类型参数在 extends 左侧单独出现,没有被其他类型包裹。只有裸参数才会触发分布。

type Naked<T> = T extends string ? "y" : "n";
// T is naked -> distributes over unions

逐步了解分布

对于 ToArray<string | number>,编译器会分别计算每个成员:

  • ToArray<string> = string[]
  • ToArray<number> = number[]

然后将它们合并为联合类型:string[] | number[]。

type R = ToArray<boolean>;
// boolean is true | false, so:
// true[] | false[]

筛选联合类型

分布让筛选变得简单。将不需要的成员映射为 never;never 会从联合类型中消失,只留下保留的成员。

type Strings<T> = T extends string ? T : never;

type A = Strings<string | number | boolean>; // string

never 会从联合类型中消失

一个关键事实是:X | never 会简化为 X。因此,当分布后的分支产生 never 时,该成员实际上会从最终的联合类型中移除。

type A = string | never;        // string
type B = "a" | never | "b";     // "a" | "b"

禁用分布

有时您不希望发生分布,例如在将整个联合类型作为一个整体进行比较时。请将两侧都包裹在只有一个元素的元组中:[T] extends [U]。此时 T 不再是裸参数,因此分布会被关闭。

type IsNever<T> = [T] extends [never] ? true : false;

type A = IsNever<never>;  // true
type B = IsNever<string>; // false

为何包裹后有效

将 [T] 放在左侧后,被检查的类型就变成了元组,而不是裸参数。特殊的分布规则不再适用,因此会将整个联合类型作为一个整体进行测试。

type AllStrings<T> = [T] extends [string] ? true : false;

type A = AllStrings<"a" | "b">;     // true
type B = AllStrings<"a" | number>;  // false

分布式与非分布式

直接比较这两种形式。裸参数版本会检查每个成员;包裹后的版本会将联合类型作为一种类型进行检查。

type Distributed<T> = T extends string ? true : false;
type Wrapped<T> = [T] extends [string] ? true : false;

type A = Distributed<string | number>; // boolean (true | false)
type B = Wrapped<string | number>;     // false

Exclude 的内部机制

内置的 Exclude<T, U> 会从 T 中移除所有可赋值给 U 的成员。它本质上只是一个分布式条件类型,将匹配的成员映射为 never。

type MyExclude<T, U> = T extends U ? never : T;

type A = MyExclude<"a" | "b" | "c", "b">; // "a" | "c"

Extract 的内部机制

Extract<T, U> 正好相反:只保留可赋值给 U 的成员。分布方式相同,但分支方向相反。

type MyExtract<T, U> = T extends U ? T : never;

type A = MyExtract<"a" | "b" | "c", "a" | "c">; // "a" | "c"

实际应用能力

分布让您可以在一个表达式中转换联合类型的每个成员:为每个成员构建数组、为每个字符串添加前缀,或按结构进行筛选。结合用于检查整个联合类型的 [T] extends [U],您就能精确控制分布何时发生。

表示法:实际的 TypeScript 会使用由反引号分隔、包含美元符号大括号占位符的字符串来书写模板字面量类型。在这些代码片段中,我们将这种模式表示为 Tpl<...>,按顺序列出各个部分;例如,与字面量前缀和随后内容 Rest 匹配的反引号模板会表示为 Tpl<'prefix', infer Rest>。

type Prefix<T extends string> = T extends unknown ? Tpl<'id_', T> : never;
// Tpl<'id_', T> is the template literal type joining 'id_' with each T

type A = Prefix<'a' | 'b'>; // 'id_a' | 'id_b'

快速检查

测试您对可分布条件类型的理解。

回顾

可分布条件类型是联合类型工具的引擎。

  • 裸的 T extends U ? 会分布到联合类型的每个成员。
  • 返回 never 的分支会筛选掉成员。
  • [T] extends [U] 会在检查整个联合类型时禁用分布。
  • Exclude 和 Extract 都是简短的可分布条件类型。

下一步进入第 22 课:类型系统中的算术。

常见问题解答

「可分配条件类型」课时是免费的吗?

是的 — 「可分配条件类型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。

「可分配条件类型」这节课中我会学到什么?

控制条件类型如何分布到联合类型上。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 TypeScript Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「可分配条件类型」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 TypeScript Academy 课中编写并运行代码吗?

能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 作为计算语言的类型
  2. 类型级条件
  3. 类型级递归
  4. 可分配条件类型
← 返回 TypeScript Academy