编程全栈开发者

Exclude在TypeScript中是如何工作的,何时使用它来处理联合类型,以及在使用这个工具类型时有哪些注意事项?

用 Hintsage AI 助手通过面试

答案。

Exclude<T, U> 是TypeScript中的一个工具类型,用于从一个类型中减去另一个类型,当需要从联合类型中排除某些值时。

问题的背景

最初在TypeScript中没有方便的方法来从一个类型中减去另一个类型。在创建泛型API或进行重构时,通常需要获取“剩余”类型——除排除值之外的所有值。需要维护多个类似接口,而不是手动处理联合类型。

问题

例如,当有类型'A | B | C'但需要得到没有B的类型时。这在构建复杂函数的输入参数、过滤允许的值和动态生成类型时经常需要。

解决方案

Exclude解决了这个问题。它的简化签名如下:

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

它返回一个类型,排除T中的所有U成员。

示例:

type Status = 'draft' | 'published' | 'removed'; type UserVisibleStatus = Exclude<Status, 'removed'>; const visible: UserVisibleStatus = 'draft'; // OK

关键特性:

  • 允许通过“减去”联合中的部分来构建动态类型。
  • 简化了重构——当基础类型更改时,所有派生类型会自动更新。
  • 可以用于过滤switch案例或对象的键。

反向问题。

可以将Exclude用于普通类型而非联合类型吗?

如果T不是联合类型,但包含在U中——Exclude仍然会起作用,但结果可能是never或T,这并不总是直观的。

Exclude<'a', 'a'> // 结果: never Exclude<'a', 'b'> // 结果: 'a'

Exclude是否删除对象结构中类型的所有引用?

不,Exclude不会递归遍历类型的嵌套字段,只在联合的顶层进行排除。

Exclude如何与接口和对象类型一起工作?

它比较整个类型,而不是单个属性。因此,Exclude从多个接口的联合中删除的只有与U完全相同的类型。

interface A { x: number }; interface B { y: string }; // Exclude<A|B, B> 结果是: A (B完全相同)

常见错误和反模式

  • 尝试将Exclude应用于嵌套或部分匹配
  • 用于“删除”接口属性,而不是联合选项
  • 忽视完全匹配类型时可能得到never类型的可能性

生活中的例子

负面案例

通过Exclude<UserRoles, 'admin'>进行用户角色验证,但忘记Exclude不适用于嵌套结构——权限'admin:sub'未被排除。

优点:

  • 角色类型的形成简单。

缺点:

  • 在嵌套或相似类型上的不明显行为;漏掉了关键角色。

正面案例

使用Exclude限制公共API的操作:Exclude<Action, 'delete'>,这排除了危险操作。

优点:

  • 在类型层面上的安全性,无法调用被禁止的操作。

缺点:

  • 需要维护最新的基础类型列表。