当前位置: 首页 > news >正文

2025最全TS手写题之partial/Omit/Pick/Exclude/Readonly/Required

随着 TS 在工作中使用的越来越广泛,面试的时候面试官也都会加上一两个 TS 的问题来了解候选人对于 TS 的熟悉程度,其中就有不少手写题目,比如笔者在字节的一次二面,面试官就问到了我如何实现一个 Pick,在小红书的一面,面试官问到了我如何实现一个 Omit。

接下来,让我们一起看看有哪些常见 TS 手写题吧。

手写 pick

Pick<T, K> ==> 从类型 T 中选出属性 K,构造成一个新的类型。

前置知识:type: 作用就是给类型起一个新名字,支持基本类型联合类型、元组及其他任何你需要的手写类型,常用于联合类型,与接口一样,用来描述对象或函数的类型。

手写实现:

type Pick<T, K extends keyof T> = {[P in K]: T[P]
}

使用示范:

// 从 User 中挑出 stature 来定义类型
interface User {stature: numberage: number
}
const people: Pick<User, "stature"> = {stature: 185
}

手写 Exclude

Exclude 是 TypeScript 的一种类型操作符,用于从类型 T 中排除掉指定的类型 K。其定义为:

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

这个定义中,使用了 TypeScript 的条件类型,它主要的义务是允许我们在类型系统中进行运算和推断。

具体来说,在这个条件类型中,我们首先声明了一个泛型类型参数 TK。然后,使用了 extends 关键字将 TK 进行对比。如果 TK 的子类型,则返回 never 类型;否则返回 T 类型。

为什么要返回 never 呢?因为 never 代表的是永远不可能出现的类型,相当于一个空集合。这样,在使用 Exclude 操作符时,我们就可以将 T 中与 K 相同的类型排除掉,从而得到一个新的类型。

例如,假设我们有以下两个类型:

type A = "a" | "b" | "c" | "d";
type B = "b" | "d" | "e" | "f";

我们可以使用 Exclude 操作符将 B 中包含的类型从 A 中排除掉,得到一个新的类型 C

type C = Exclude<A, B>; // "a" | "c"

因为 A 中包含了四个类型,而 B 中包含了 "b""d",所以将这两个类型从 A 中排除后,剩余的类型就是 "a""c"。因此,新的类型 C 就是 "a" 或者 "c"

手写 partial

Partial 是 TypeScript 内置的一个类型操作符,它用于将某个类型中每个属性设置为可选属性,这表示这些属性的值可以是 undefined 或者省略。

下面是 Partial 的代码:

type Partial<T> = {[P in keyof T]?: T[P];
};

这个定义使用了 TypeScript 中的映射类型(Mapped Types)和索引访问类型(Index Access Types)。

首先,声明一个泛型类型 T 作为待操作的类型。然后,使用了映射类型语法,声明一个新类型,其属性名为 P,该属性名必须是 T 的属性名之一,属性值为该属性名在 T 类型中对应的类型。并且使用 ? 符号来将所有属性设置为可选属性。

该类型操作符的作用是让某个类型中的每个属性都变成可选属性,从而灵活地处理类型的使用,特别是在一些属性不是必需的场景下。

上面的示例中,我们先声明了一个 Person 接口,然后使用 Partial 操作符将其转化为可选类型 PartialPerson,接着创建三个可选类型的变量 person1person2person3。在 person1 中,只设置了 name 属性,而 age 属性是可选的,其值默认为 undefined;在 person2 中,没有设置任何属性,两个属性都是可选的,其值默认为 undefined;在 person3 中,设置了 nameage 两个必选属性,其值和 Person 类型一致。

手写 Omit

Omit 是 TypeScript 的一种类型操作符,用于从类型 T 中删去指定的属性 K。其定义为:

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;

这个定义中,使用了 TypeScript 的类型推导机制和 Exclude 操作符。

首先,我们声明了两个泛型类型参数 TK,其中 T 表示要操作的类型,K 表示要删去的属性名。

然后,使用了 keyof T 操作符,获取了 T 类型的所有属性名组成的联合类型。接着,使用 Exclude 操作符将 K 从这个联合类型中排除掉,得到一个新的属性名联合类型。最后,使用 Pick 操作符从 T 类型中选取删去了属性 K 后的属性集合。

例如,假设我们有以下类型:

interface Person {name: string;age: number;address: {city: string;street: string;};
}type PersonNameAndAge = Omit<Person, "address">;

PersonNameAndAge 将会是一个新的类型,它只包含 Person 中的 nameage 两个属性。可以通过以下方式检查它的类型:

const person: PersonNameAndAge = { name: "Alice", age: 30 };
person.name = "Bob"; // 可以修改
person.address = { city: "Shanghai", street: "Nanjing Road" }; // 报错,address 属性不存在

因此,Omit 操作符可以方便地帮助我们从一个类型中删去指定的属性,得到一个新的类型。

手写 Readonly

Readonly 是 TypeScript 内置的一个类型操作符,它用于将某个类型中每个属性设置为只读属性,这表示这些属性的值不能被修改。

下面是 Readonly 的代码:

type Readonly<T> = {readonly [P in keyof T]: T[P];
};

这个定义使用了 TypeScript 中的映射类型(Mapped Types)和索引访问类型(Index Access Types)。

首先,声明一个泛型类型 T 作为待操作的类型。然后,使用了映射类型语法,声明一个新类型,其属性名为 P,该属性名必须是 T 的属性名之一,属性值为该属性名在 T 类型中对应的类型。并且使用 readonly 关键字将属性设置为只读属性。

该类型操作符的作用是保护类型中的属性免于被错误的修改,特别是防止在多个地方引用该类型中的同一个属性时产生冲突。

以下是一个使用 Readonly 操作符的示例:

interface Person {name: string;age: number;
}type ReadonlyPerson = Readonly<Person>;let person: ReadonlyPerson = { name: "Alice", age: 30 };
person.name = "Bob"; // 报错,因为 name 是只读属性
person.age = 40; // 报错,因为 age 是只读属性
person = { name: "Carol", age: 50 }; // 可以修改整体属性值

上面的示例中,我们先声明了一个 Person 接口,然后使用 Readonly 操作符将其转化为只读类型 ReadonlyPerson,接着创建一个只读类型的变量 person,并赋初值为 { name: "Alice", age: 30 }。由于 ReadonlyPerson 中的每个属性都是只读属性,所以我们不能修改 person 中的任何属性。如果要修改整个数据对象,我们需要分配一个新对象来覆盖 person

手写 Required

Required 是 TypeScript 内置的一个类型操作符,它用于将某个类型的所有可选属性都转换为必选属性。

下面是 Required 的代码:

type Required<T> = {[P in keyof T]-?: T[P];
};

这个定义使用了 TypeScript 中的映射类型(Mapped Types)和索引访问类型(Index Access Types)。

首先,声明一个泛型类型 T 作为待操作的类型。然后,使用了映射类型语法,声明一个新类型,其属性名为 P,该属性名必须是 T 的属性名之一,属性值为该属性名在 T 类型中对应的类型。并在属性名前使用 -? 符号来将所有属性设置为必选属性。

该类型操作符的作用是保证某个类型中的每个属性都必须有值,从而避免在使用该类型的地方出现意料之外的错误或者异常情况。

下面是一个使用 Required 操作符的示例:

interface Person {name?: string;age?: number;
}type RequiredPerson = Required<Person>;let person: RequiredPerson = { name: "Alice", age: 30 };
person.name = "Carol"; // 正常
person.age = 50; // 正常
person = { }; // 报错,因为 name 和 age 是必选属性

上面的示例中,我们先声明了一个 Person 接口,然后使用 Required 操作符将其转化为必选类型 RequiredPerson,接着创建一个必选类型的变量 person,并赋初值为 { name: "Alice", age: 30 }。由于 RequiredPerson 中的每个属性都是必选属性,所以我们必须设置 person 中的所有属性。如果我们忘记设置某个属性,或者设置错误的属性名,就会在编译期间发生错误,避免出现运行期错误。

相关文章:

  • STM32使用水位传感器
  • 牛客round95D
  • 科伦药业:以“三发引擎”驱动创新,全面迈入价值收获新周期
  • TCA 循环中间体如何改写肝损伤命运【AbMole】
  • VAS1086Q 奇力科技线性芯片车规用品LED驱动芯片
  • 无人机EN 18031欧盟网络安全认证详细解读
  • ABAP设计模式之---“童子军法则(The Boy Scout Rule)”
  • 最短路径算法总结
  • 卡尔曼滤波器:从概念到应用
  • ICDAR数据集简介
  • RAID存储技术概述
  • 破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
  • 2025年- H79-Lc187--118. 杨辉三角(找规律)--Java版
  • 初识Docker——容器化革命核心概念
  • LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
  • 【Proteus仿真】【32单片机-A010】步进电机控制系统设计
  • 前端与服务器交互以及前端项目组成。
  • 了解Android studio 初学者零基础推荐(4)
  • 计算机视觉一些定义解析
  • RK3588开发笔记-wifi6 SDIO接口rtl8822cs调试笔记
  • 个人网站的设计与实现毕业论文5000字/优云优客百度推广效果怎么样
  • 网站的主要内容/湖北疫情最新消息
  • cms开发是什么意思/江苏seo外包
  • 顺德区网站设计建设企业/怎么让网站被百度收录
  • 免费试用网站空间/近一周热点新闻
  • 无网站做cpa推广/公司推广渠道有哪些