Skip to content

TS 中, 使用 Omit 和将属性类型设为 never 的区别 #79

@inkjuncom

Description

@inkjuncom

在 TypeScript 中,使用 Omit 和将属性类型设为 never 虽然都能达到“不使用某个属性”的目的,但它们的底层逻辑类型表现以及适用场景有本质的区别。

简单总结:

  • Omit 是物理上的**“删除”**:该属性在类型中彻底不存在了。
  • never 是逻辑上的**“禁用”**:该属性还在,但无法给它赋值(通常用于互斥场景)。

1. 使用 Omit 排除属性

这是最标准、最常用的方法。它会生成一个新的类型,完全移除指定的键。

用法

interface User {
  name: string;
  email: string;
  adminToken: string;
}

// 彻底移除 adminToken
type PublicUser = Omit<User, 'adminToken'>;

/*
  PublicUser 的结构等同于:
  {
    name: string;
    email: string;
  }
*/

表现

  1. 赋值检查:如果在对象字面量中包含该属性,会报错(Object literal may only specify known properties)。
  2. 访问检查:尝试访问该属性(user.adminToken)会直接报错,因为属性不存在。
  3. IntelliSense:代码提示中不会出现该属性。

2. 使用 never 排除属性

这种方式通常保留属性名,但将其类型设为 never(通常配合可选修饰符 ?),表示“这个属性不应该有值”。

用法

interface User {
  name: string;
  email: string;
  // 将 adminToken 设为可选且为 never
  adminToken?: never; 
}

const user1: User = {
  name: "Xiaomei",
  email: "test@example.com"
  // ✅ 合法:不写 adminToken
};

const user2: User = {
  name: "Xiaomei",
  email: "test@example.com",
  // ❌ 报错:Type 'string' is not assignable to type 'never'.
  adminToken: "123" 
};

表现

  1. 赋值检查:你可以写这个属性名,但不能给它赋任何值(除了 undefined)。
  2. 访问检查:访问该属性是合法的,但得到的类型是 undefined
  3. 类型结构:该属性依然存在于 keyof User 中。

3. 核心区别对比

维度 Omit (删除) never (禁用)
属性是否存在 不存在keyof T 中不再包含该键。 存在keyof T 仍包含该键。
代码提示 不会提示该属性。 会提示该属性,但类型显示为 never
访问属性 报错:Property does not exist 允许访问,但通常只能得到 undefined
主要用途 裁剪类型,生成子集(DTO、Props)。 互斥类型(XOR),禁止某些属性组合。

4. 什么时候用哪个?

场景 A:只需要一部分数据(用 Omit)

如果你从后端获取了一个大对象,但前端组件只需要其中的一部分,或者你要把数据传给一个不应该知道 idpassword 的函数。
请使用 Omit

// 这里的 props 不需要 id,因为是新建用户
type CreateUserProps = Omit<User, 'id'>;

场景 B:属性互斥(用 never)

这是 never 最强大的场景。假设一个组件,要么接受 text,要么接受 children,但不能同时接受。

如果用 Omit 很难表达“二选一”的关系,但用 never 可以实现 Discriminated Unions(可辨识联合) 的变体:

// 方案:使用 never 实现互斥 (XOR)
type TextProps = {
  text: string;
  children?: never; // ⛔️ 禁止传 children
};

type ChildrenProps = {
  text?: never;     // ⛔️ 禁止传 text
  children: React.ReactNode;
};

type ButtonProps = TextProps | ChildrenProps;

// ✅ 合法
const b1: ButtonProps = { text: "Click me" };
const b2: ButtonProps = { children: <span>Click me</span> };

// ❌ 报错:不能同时存在
const b3: ButtonProps = { 
  text: "Click", 
  children: <span>Me</span> 
}; 
// 报错原因:类型不能同时满足 TextProps (children必须是undefined) 和 ChildrenProps (text必须是undefined)

总结

  • 如果你只是想**“清理”**类型,让它变干净,用 Omit
  • 如果你是想**“限制”**类型,防止用户同时传递冲突的属性,用 never(配合联合类型)。

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions