Ts中的泛型函数总结keyof

本文详细讲解了 TypeScript 中的关键操作符 keyof,以及 Partial、Required、Readonly 和 Omit 的功能和用法示例,帮助开发者更好地操纵类型和创建灵活的类型系统。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

keyof

TypeScript中的keyof操作符,是将一个类型映射为它所有成员名称的联合类型。

interface Person {
  name: string;
  age: number;
  gender: string;
}
type P = keyof Person; // "name" | "age" | "gender"

// 我们可以看到,keyof将Person这个对象类型映射成了一个联合类型
// 因此我们可以更方便的操作这个联合类型
Partial

Partial的作用是将传入的属性变成可选项,原理就是使用keyof拿到所有属性名,然后再使用in[遍历],T[P]拿到相应的值。

type Partial<T> = { [P in keyof T]?: T[P] }
//作用:生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为可选项

看看使用实列

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

Required

Required 的作用是将传入的属性变为必选项,原理是使用-?将可选项的?去掉。与之对应的还有个+?。

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

例子

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

Readonly的作用是将传入的属性变为只读选项

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

Mutable的作用是将传入属性的readonly移除。

This is useful as a lightweight builder for a type with readonly properties (especially useful when some of the properties are optional and conditional logic is needed before assignment).

type Mutable<T> = {
  -readonly[P in keyof T]: T[P]
};
type Mutable<T> = {-readonly[P in keyof T]: T[P]};
 interface Foobar {
   readonly a: number;
   readonly b: number;
   readonly x?: string;
 }

 function newFoobar(baz: string): Foobar {
   const foobar: Mutable<Foobar> = {a: 1, b: 2};
   if (shouldHaveAnX(baz)) {
     foobar.x = 'someValue';
   }

   return foobar;
 }

Record

Record的作用是将K中所有属性的值转化成T类型

Record<K,T>构造具有给定类型T的一组属性K的类型。在将一个类型的属性映射到另一个类型的属性时,Record非常方便。他会将一个类型的所有属性值都映射到另一个类型上并创造一个新的类型.

type Record<K extends keyof any, T> = {
    [P in K]: T;
};
type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
    name:string,
    age:number,
}

type IPets = Record<petsGroup, IPetInfo>;

const animalsInfo:IPets = {
    dog:{
        name:'dogName',
        age:2
    },
    cat:{
        name:'catName',
        age:3
    },
    fish:{
        name:'fishName',
        age:5
    }
}
Pick

Pick的作用是从T中取出一系列K的属性

type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
interface Person {
  name: string;
  age: number;
  id: number;
  sex: 0 | 1;
}

// 问女生年纪不太礼貌,所以我们不需要 age 这个属性
type Woman = Pick<Person, "name" | "id">;

// 此时 Woman 等效于 Female

interface Female {
  name: string;
  id: number;
}
Exclude

Exclude的作用是从T中找出U中没有的元素

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

type A = Exclude<'key1' | 'key2', 'key2'> 
// 'key1'

这个定义就利用了条件类型中的分配原则,来尝试将实例拆开看看发生了什么:

type A = `Exclude<'key1' | 'key2', 'key2'>`

// 等价于

type A = `Exclude<'key1', 'key2'>` | `Exclude<'key2', 'key2'>`

// =>

type A = ('key1' extends 'key2' ? never : 'key1') | ('key'2 extends 'key2' ? never : 'key2')

// =>

// never是所有类型的子类型
type A = 'key1' | never = 'key1'
Extract

高级类型Extract和上面的Exclude刚好相反,它是将第二个参数的联合项从第一个参数的联合项中提取出来,当然,第二个参数可以含有第一个参数没有的项。

下面是其定义和一个例子,有兴趣可以自己推导一下

type Extract<T, U> = T extends U ? T : never 
type A = Extract<'key1' | 'key2', 'key1'> // 'key1'
Omit

Omit的作用是忽略对象的某些属性功能
它的作用主要是:以一个类型为基础支持剔除某些属性,然后返回一个新类型。

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Person = {
    name: string;
    age: string;
    location: string;
};

type PersonWithoutLocation = Omit<Person, 'location'>;

// PersonWithoutLocation equal to QuantumPerson
type QuantumPerson = {
    name: string;
    age: string;
};
<think>嗯,用户想了解 TypeScript 中的型。首先,我需要回忆型的基本概念。型在 TS 中是用来创建可复用组件的,允许类型作为参数,提高代码的灵活性和安全性。用户可能已经知道一些类型基础,但想深入型。 接下来,用户的应用场景可能是什么?他们可能在写函数或类时需要处理多种数据类型,但不想重复代码。比如,一个处理数组或API响应的函数。这时候型就能派上用场,避免使用 any 类型失去类型检查的好处。 用户可能的疑问点包括型语法、使用场景、高级用法如型约束、默认类型等。需要分步骤解释,从简单例子入手,逐步深入。比如先展示没有型的问题,再引入型解决方案。 要注意用户可能对类型参数的理解有困难,需要用类比,比如函数的参数但作用于类型。同时,要强调型的好处:代码复用和类型安全。举例说明,比如 identity 函数,用 any 会丢失类型信息,用型则保留。 可能还需要提到型在接口、类中的应用,以及常见工具类型如 Partial、Readonly 的实现原理。此外,约束和默认值也是重要部分,比如 extends 和 keyof 的使用,帮助用户处理更复杂的情况。 需要检查是否有常见的错误理解,比如认为型会增加运行时开销,但实际上型只在编译时起作用。还要提醒类型推断的情况,有时候不需要显式指定类型参数,编译器会自动推断。 最后,总结使用型的最佳实践,比如何时使用,如何保持代码简洁,避免过度使用导致代码难以理解。确保用户不仅知道怎么用,还知道何时用,以及背后的设计理念。</think>好的,我来为您逐步讲解 TypeScript 中的型(Generics): # 一、为什么需要型? 假设我们要写一个返回参数本身的函数: ```typescript // 使用 any 的写法(不安全) function identity(arg: any): any { return arg } ``` - ✅ 可以接受任意类型 - ❌ 丢失了类型信息(输入 number 会被推断为 any) # 二、型的基本使用 用类型变量 `<T>` 解决上述问题: ```typescript // 型写法(保持类型) function identity<T>(arg: T): T { return arg } // 使用示例 const output1 = identity<string>("hello") // 显式指定类型 const output2 = identity(42) // 自动类型推断(推荐) ``` - `T` 会捕获传入的类型,并在整个函数中保持这个类型 - 支持类型推断,通常不需要显式指定 # 三、型的核心特点 1. **类型参数化**:把类型当作参数传递 2. **代码复用**:一套逻辑支持多种类型 3. **类型安全**:编译时进行类型检查 # 四、常见应用场景 ## 1. 处理数组 ```typescript function getFirst<T>(arr: T[]): T { return arr[0] } // 使用 const num = getFirst([1, 2, 3]) // number 类型 const str = getFirst(["a", "b", "c"]) // string 类型 ``` ## 2. 接口中使用 ```typescript interface ApiResponse<T> { code: number message: string data: T } // 使用 const userResponse: ApiResponse<{name: string, age: number}> = { code: 200, message: "success", data: { name: "Alice", age: 30 } } ``` ## 3. 类中使用 ```typescript class Box<T> { private content: T constructor(value: T) { this.content = value } getValue(): T { return this.content } } // 使用 const numberBox = new Box<number>(42) const stringBox = new Box("hello") // 自动推断为 string ``` # 五、高级用法 ## 1. 型约束(Generic Constraints) ```typescript interface HasLength { length: number } function logLength<T extends HasLength>(arg: T): void { console.log(arg.length) } logLength("hello") // 5 logLength([1,2,3]) // 3 logLength(123) // 错误:number 没有 length 属性 ``` ## 2. 使用类型参数 ```typescript function getProperty<T, K extends keyof T>(obj: T, key: K) { return obj[key] } const person = { name: "Bob", age: 25 } getProperty(person, "age") // 正确 getProperty(person, "email") // 错误:email 不存在于 person ``` ## 3. 默认类型参数 ```typescript interface Pagination<T = string> { data: T[] pageSize: number total: number } // 使用默认类型 const strPagination: Pagination = { /*...*/ } // 自定义类型 const numPagination: Pagination<number> = { /*...*/ } ``` # 六、最佳实践建议 1. 优先让 TypeScript 自动推断类型 2. 当需要类型关系时才使用型 3. 使用有意义的类型参数名称(如 T, U, V 或更具体的名称) 4. 避免过度使用型导致代码可读性下降 # 七、常见工具类型原理 这些内置工具类型都是基于型实现的: ```typescript type Partial<T> = { [P in keyof T]?: T[P] } type Readonly<T> = { readonly [P in keyof T]: T[P] } type Pick<T, K extends keyof T> = { [P in K]: T[P] } ``` 通过型,我们可以编写类型安全且高度复用的代码,这是 TypeScript 类型系统中非常强大的特性。建议从简单场景开始实践,逐步掌握更复杂的用法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值