实现MyExclude<T, U>排除联合类型

从 T 联合类型中移除可赋值给 U 的类型

用例

type Test= MyExclude<"a"|"b"|"c", "a">;
//预期:"b"|"c"

实现

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

知识点

条件类型中的分发特性

  • 它不是指"联合类型"本身有什么分发行为, 而是指: 当联合类型遇到条件类型(T extends U ? X: Y )时,Typescript会自动将联合类型的每个成员分别传入条件类型进行判断,最后在将结果合并回联合类型. 当然这种分发不是没有条件.
  • 触发条件
    • 类型参数 T 是一个 裸类型 参数(没有被包裹在数组, 元组, 函数参数等容器中)
    • T 是一个联合类型(如string|number|"abc")
    • 使用 T extends U ? X: Y的条件类型语法
  • 通用表达式(我的理解, 不知道对不对)
    • 如果 T = A | B| C , 那么 T extends U ? X : Y 等价于(A extends U ? X : Y ) | (B extends U ? X : Y ) | (C extends U ? X : Y )

Never在联合类型中会自动消失

type A = string|number|never
// A:string|number

分发特性举例

type ToArray<T> = T extends any ? T[]: never;
type Res =ToArray<string|number>; // 因为分发特性机制, ToArray分别对string和number进行运算 (string extends any ? string[]:never)|(number extends any ? number[]:never ) ,最后结果 string[]|number[]
// Res: string[]|number[]

如何"阻止" 分发

如果不想让联合类型分开运算, 希望整体处理, 只需要用元组或数组把 T包裹起来, 破坏裸类型的参数条件

type ToArray<T> = [T] extends any ? T[]: never;// 主要看extends 左侧参数是否被包装过
type Res =ToArray<string|number>;
//Res:(string|number)[]

联合类型分发特性 = 条件类型在遇到裸联合类型参数时,会自动执行拆包 → 逐个判断 → 重新打包的隐式逻辑。也是为什么MyExclude 可以进行过滤的重要原因. TypeScript实现高阶类型工具(如 Exclude、Omit、ReturnType)的底层原理.

posted @ 2026-07-11 20:55  一个行走者  阅读(10)  评论(0)    收藏  举报