TS (Record、Partial 、 Required 、 Readonly、 Pick 、 Exclude 、 Extract 、 Omit)常用的工具类型的使用

Record

Record的内部定义,接收两个泛型参数;Record后面的泛型就是对象键和值的类型

作用 :义一个对象的 key 和 value 类型

源码:

Record<key type, value type> 
Record<string, never> 空对象
Record<string, unknown> 任意对象
{} 任何不为空的对象

type Record<K extends string | number | symbol, T> = {
    [P in K]: T;
}

逐步解析

泛型K即为第一次参数
p in xx 又是什么意思呢?
in的意思就是遍历,如上就是将 类型string进行遍历,也就是string
每个属性都是传入的T类型,如 string: PersonModel

Partial

源码:

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

作用:生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为可选项

interface Foo {
    name: string
    age: number
}
type Bar = Partial<Foo>
// 相当于
type Bar = {
    name?: string
    age?: number
}

Required

源码:

type Require<T> = {
    [p in keyof T]-?: T[P]
}

作用:生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为必选项

interface Foo {
    name: string
    age?: number
}
type Bar = Required<Foo>
// 相当于
type Bar = {
    name: string
    age: string
}

Readonly

源码:

type Require<T> = {
    [p in keyof T]-?: T[P]
}

作用:生成一个新类型,T 中的 K 属性是只读的,K 属性是不可修改的。

interface Foo {
    name: string
    age: number
}
type Bar = Required<Foo>
// 相当于
type Bar = {
    readonly name: string
    readonly age: string
}

Pick

源码:

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

作用:生成一个新类型,该类型拥有 T 中的 K 属性集 ; 新类型 相当于 TK 的交集

interface Foo {
    name: string;
    age?: number;
    gender: string;
}
type Bar = Pick<Foo, 'age' | 'gender'>
// 相当于
type Bar = {
    age?: number
    gender: string
}

const todo: Bar= {
   age?: 3,
   gender:};
console.log(todo)

Exclude

源码:

type Exclude<T, U> = T extends U ? never : T
1
作用:如果 TU 的子类型则返回 never 不是则返回 T

type A = number | string | boolean
type B = number | boolean

type Foo = Exclude<A, B>
// 相当于
type Foo = string

Extract

源码:

type Extract<T, U> = T extends U ? T : never

作用: 和 Exclude 相反

type A = number | string | boolean
type B = number | boolean

type Foo = Extract<A, B>
// 相当于
type Foo = number | boolean

Omit

源码定义:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>> 

作用:生成一个新类型,该类型拥有 T 中除了 K 属性以外的所有属性

type Foo = {
	name: string
	age: number
}

type Bar = Omit<Foo, 'age'>
// 相当于
type Bar = {
	name: string
}

NonNullable

源码定义:

NonNullable<T>

作用:从泛型 T 中排除掉 nullundefined

type NonNullable<T> = T extends null | undefined ? never : T;

type t = NonNullable<'name' | undefined | null>;
/* type t = 'name' */

Parameters

源码定义:

Parameters<T extends (...args: any) => any>

作用:以元组的方式获得函数的入参类型

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

type t = Parameters<(name: string) => any>; // type t = [string]

type t2 = Parameters<((name: string) => any)  | ((age: number) => any)>; // type t2 = [string] | [number]

ConstructorParameters

源码定义:

ConstructorParameters<T extends new (...args: any) => any>

作用:以元组的方式获得构造函数的入参类型

type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
 
type t = ConstructorParameters<(new (name: string) => any)  | (new (age: number) => any)>;
// type t = [string] | [number]

ReturnType

源码定义:

ReturnType<T extends (...args: any) => any>

作用:获得函数返回值的类型

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
 
type t = ReturnType<(name: string) => string | number>
// type t = string | number

InstanceType

源码定义:

InstanceType<T extends new (...args: any) => any>
1
作用:获得构造函数返回值的类型

type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
 
type t = InstanceType<new (name: string) => {name: string, age: number}>
/* 
type h = {
    name: string;
    age: number;
}
*/
<think> 首先,用户的问题是:从一个专业的前端开发工程师来有逻辑地详细讲解一下TS中的partialpickreadonlyrecord内置高级类型,并做总结。我需要用中文回答。 我的回答必须包括: - 详细且有逻辑地讲解这些内置高级类型。 - 做一个总结。 - 在回答完毕后,必须提出3-5个相关问题。 - 相关问题必须在末尾输出,第一行必须是"§§相关问题§§",禁止换行分散,输出格式如示例所示。 关键内置高级类型: - `Partial<T>`:创建一个新类型,其中`T`的所有属性都变为可选。 - `Pick<T, K extends keyof T>`:从`T`中选择一些属性`K`来创建新类型。 - `Readonly<T>`:创建一个新类型,其中`T`的所有属性都变为只读。 - `Record<K, T>`:创建一个对象类型,其键的类型为`K`,值的类型为`T`。 我需要以专业前端开发工程师的角度来讲解,所以应该结合实际应用场景,比如在React组件或状态管理中如何使用这些类型。 结构回答: 1. **介绍**:简要概述这些内置类型的作用。 2. **逐个讲解**: - `Partial<T>`:解释定义、用途(如处理可选配置)。 - `Pick<T, K>`:解释定义、用途(如选择特定属性)。 - `Readonly<T>`:解释定义、用途(如防止意外修改)。 - `Record<K, T>`:解释定义、用途(如映射键到值)。 3. **总结**:归纳这些类型的共同点、用途,强调在TypeScript中的重要性。 4. **相关问题**:提出3-5个相关问题,必须与主题相关,格式如示例。 问题数量:3-5个,我准备4个。 相关问题想法: 1. 如何在React中使用`Pick`来选择组件props? 2. `Partial`和可选属性有什么区别? 3. 为什么`Readonly`在函数参数中很有用? 4. `Record`与索引签名的区别是什么? 5. 这些内置类型如何帮助类型安全? 确保问题用中文,格式:"1. ···" 等,一行一个。 最后,输出: - 回答内容。 - 然后"§§相关问题§§" - 接着相关问题列表。 禁止换行分散,所以相关问题必须连续输出。 完整回答: </think> ### 专业前端开发工程师讲解 TypeScript 内置高级类型 作为专业的前端开发工程师,我在实际项目(如 React、Angular 或 Vue 应用)中经常使用 TypeScript 的高级类型来提升代码的类型安全性和可维护性。这些内置类型基于 TypeScript 的映射类型(Mapped Types)实现,能帮助我们高效地操作对象类型。下面我将有逻辑地详细讲解 `Partial`、`Pick`、`Readonly` 和 `Record`,并结合前端场景示例说明其用法和价值。 #### 1. `Partial<T>` - **定义与作用**:`Partial<T>` 创建一个新类型,该类型将 `T` 的所有属性都设为可选(即每个属性添加 `?` 修饰符)。它本质上是一个映射类型:`type Partial<T> = { [P in keyof T]?: T[P]; }`。 - **核心逻辑**:通过遍历 `T` 的所有属性键(`keyof T`),并为每个属性添加可选标记,避免强制要求所有属性必须存在。 - **前端应用场景**:在表单处理或 API 请求中非常有用。例如,在 React 中更新状态时,你可能只需要修改对象的部分属性: ```typescript interface User { id: number; name: string; email: string; } // 使用 Partial 允许只传入部分属性 const updateUser = (updateData: Partial<User>) => { // 逻辑代码... }; // 调用时可只传部分字段 updateUser({ name: "张三" }); // 合法,无需提供所有属性 ``` - **优点**:提升代码灵活性,减少样板代码,避免因遗漏属性导致的错误。 #### 2. `Pick<T, K extends keyof T>` - **定义与作用**:`Pick<T, K>` 从类型 `T` 中选择指定的一组属性键 `K`(必须是 `T` 的键的子集),创建一个新类型类型定义为:`type Pick<T, K extends keyof T> = { [P in K]: T[P]; }`。 - **核心逻辑**:通过指定键集合 `K`,只保留 `T` 中这些键对应的属性,其他属性被忽略。 - **前端应用场景**:在组件 props 或状态管理中,用于精简类型。例如,在 React 组件中,你可能只需要父组件传递特定属性: ```typescript interface Product { id: number; name: string; price: number; description: string; } // 使用 Pick 只选择需要的属性 type ProductSummary = Pick<Product, 'id' | 'name' | 'price'>; const ProductCard: React.FC<ProductSummary> = ({ id, name, price }) => { // 显示产品摘要 return <div>{name} - ${price}</div>; }; // 调用时只需提供选中属性 <ProductCard id={1} name="手机" price={2999} />; // 合法 ``` - **优点**:提高组件重用性和类型约束精确度,减少不必要的属性传递。 #### 3. `Readonly<T>` - **定义与作用**:`Readonly<T>` 创建一个新类型,该类型将 `T` 的所有属性设为只读(即添加 `readonly` 修饰符)。类型定义为:`type Readonly<T> = { readonly [P in keyof T]: T[P]; }`。 - **核心逻辑**:遍历 `T` 的每个属性,添加 `readonly` 关键字,确保属性值在创建后不可修改。 - **前端应用场景**:适用于不可变数据,如 Redux 状态或配置对象。在 React 中,防止意外修改 props 或状态: ```typescript interface AppConfig { apiUrl: string; theme: 'light' | 'dark'; } // 使用 Readonly 确保配置不被修改 const config: Readonly<AppConfig> = { apiUrl: 'https://api.example.com', theme: 'light', }; // config.apiUrl = 'https://new-api.com'; // 错误: 无法分配给只读属性 // 在函数参数中使用 const initializeApp = (config: Readonly<AppConfig>) => { // 安全读取 config,防止内部修改 }; ``` - **优点**:增强不可变性,避免副作用,提升并发安全性。 #### 4. `Record<K, T>` - **定义与作用**:`Record<K, T>` 创建一个对象类型,其中键为类型 `K`(通常是字符串或数字联合类型),值为类型 `T`。类型定义为:`type Record<K extends keyof any, T> = { [P in K]: T; }`。 - **核心逻辑**:映射键集合 `K` 到统一的值类型 `T`,常用于动态键值结构。 - **前端应用场景**:在字典或映射数据时非常高效,如国际化文案或状态映射。例如,在 Vue 或 React 中管理错误消息: ```typescript // 定义错误代码类型 type ErrorCode = '404' | '500' | '403'; // 使用 Record 创建错误消息映射 const errorMessages: Record<ErrorCode, string> = { '404': '页面未找到', '500': '服务器错误', '403': '权限不足', }; // 在组件中使用 const getErrorMessage = (code: ErrorCode) => errorMessages[code]; // 也适用于 API 响应映射 type UserRoles = 'admin' | 'user'; const rolePermissions: Record<UserRoles, string[]> = { admin: ['create', 'delete'], user: ['read'], }; ``` - **优点**:简化动态对象的类型声明,确保键值一致性,提升代码可读性。 ### 总结 - **共同点**:这些高级类型都基于映射类型机制,通过遍历和转换现有类型来创建新类型,增强了 TypeScript 的类型操作能力。它们与泛型结合,能灵活适配多种场景。 - **前端价值**: - 提升类型安全:减少运行时错误,如在表单验证或状态管理中捕获类型问题。 - 优化代码维护:通过精简或扩展类型,降低重复代码,尤其在大规模项目中。 - 增强开发效率:IDE 提示更精确,重构更可靠。 - **最佳实践**:在 React props、Redux reducers 或 API 接口定义中优先使用这些类型。例如,结合 `Partial` 处理可选更新,`Record` 管理配置字典。 - **局限与注意**:过度使用可能导致类型复杂性增加;需确保键约束正确(如 `keyof T`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值