TypeScript实用笔记(三):泛型<T>的使用 <T>的12种工具类型的使用
文章目录
- 一、理解
- 二、使用
- 1. 泛型函数
- 1.1 单一类型参数的泛型函数
- 1.2 泛型数组
- 1.3 多个类型参数的泛型函数
- 1.4 泛型约束
- 1.5 默认类型参数
- 1.6 使用泛型约束和函数重载
- 2. 泛型接口
- 2.1 单类型参数的泛型接口
- 2.2 多类型参数的泛型接
- 2.3 泛型约束
- 2.4 默认类型参数
- 2.5 复杂类型
- 3. 泛型类
- 3.1 单类型参数的泛型类
- 3.2 多个类型参数的泛型类
- 3.3 泛型约束
- 3.4 默认类型参数
- 3.5 复杂类型
- 三、工具类型
- 1. Partial\<T>: 将对象类型的所有属性变为可选
- 2. Required\<T>: 将对象类型的所有属性变为必选
- 3. Readonly\<T>: 将对象类型的所有属性变为可读
- 4. Record\<K, T>: 创建一个对象类型
- 5. Pick\<T, K>: 从对象类型中选取一组属性
- 6. Omit\<T, K>: 从对象类型中排除指定的一组属性
- 7. Extract\<T, U>:提取联合类型中并存的类型
- 8. Exclude\<T, U>: 从一个联合类型中排除另一个联合类型的成员
- 9. NonNullable\<T>: 排除类型中的null和undefined
- 10. ReturnType\<T>: 获取函数的返回类型
- 11. InstanceType\<T>: 获取构造函数的实例类型
- 12. ThisType\<T>: 提供对上下文this的类型支持
TypeScript实用笔记(一):初始化、类型定义与函数使用
TypeScript实用笔记(二):类与接口详解
TypeScripttype实用笔记(三):泛型的使用 && 的12种工具类型的使用
一、理解
在TypeScript
中初始化使用function
时,就需要直接确定接收参数的类型、返回值的类型。而泛型<T>
的出现,则可以让参数和返回值的类型变得更加灵活。不再是初始化function
时去确定类型,而是在调用该方法的时候传类型。
[注意:<T>这个T可以是任意字母,这里只是type首字母的缩写,省事儿就用了]
二、使用
1. 泛型函数
1.1 单一类型参数的泛型函数
- 可以定义接受类型参数的函数
function identity<T>(arg: T): T {
return arg;
}
const result1 = identity<number>(42); //指定类型为 number
const result2 = identity<string>("Hello"); //指定类型为 string
console.log(result1); //输出: 42
console.log(result2); //输出: Hello
1.2 泛型数组
//<T>中的T表示单一的类型。而 T[] 表示该类型的数组。因此,定义一个接受数组的泛型函数是完全合法的。但是不允许在<>中添加额外的,例如<T[]>是非法的
function identity<T>(arg: T[]): T[] {
return arg;
}
const numberArray = identity<number>([1, 2, 3]); //T 被推断为 number
const stringArray = identity<string>(["Hello", "World"]); //T 被推断为 string
const numAndStr = identity<string | number>(["Hello", "World", 3]); // T 被推断为 string or number
console.log(numberArray); // 输出: [1, 2, 3]
console.log(stringArray); // 输出: ["Hello", "World"]
1.3 多个类型参数的泛型函数
- 可以定义一个接受多个类型参数的函数。
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
const result = pair<string, number>("age", 30);
console.log(result); //输出: ['age', 30]
1.4 泛型约束
- 可以为泛型参数添加约束,以确保其满足某个接口或类型的条件。
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): void {
console.log(arg.length);
}
logLength("Hello"); //正确,字符串有 length 属性
logLength([1, 2, 3]); //正确,数组有 length 属性
//logLength(123); //错误,数字没有 length 属性
1.5 默认类型参数
function createArray<T = number>(length: number): T[] {
return new Array(length);
}
const numberArray = createArray(5); //默认类型为 number
const stringArray = createArray<string>(5); //指定类型为 string
console.log(numberArray); //输出: [ <5 empty items> ]
console.log(stringArray); //输出: [ <5 empty items> ]
1.6 使用泛型约束和函数重载
- 可以接口泛型约束和函数重载来处理不同的输入类型
//定义一个接口,包含一个 name 属性
interface Named {
name: string;
}
//定义一个泛型函数重载,接受一个 T 类型的参数并返回一个 string 类型的值
function greet<T extends Named>(value: T): string;
function greet(value: string): string;
//实现泛型函数,接受一个 T 类型的参数并返回一个 string 类型的值
function greet(value: any): string {
if (typeof value === "string") {
return `Hello, ${value}`;
}
return `Hello, ${value.name}`;
}
//调用 greet 函数,传入一个字符串,输出: Hello, Alice
console.log(greet("Alice")); //输出: Hello, Alice
//调用 greet 函数,传入一个包含 name 属性的对象,输出: Hello, Bob
console.log(greet({ name: "Bob" })); //输出: Hello, Bob
//调用 greet 函数,传入一个不包含 name 属性的对象会报错
//console.log(greet({ age: 30 })); //错误: 类型 '{ age: number; }' 不满足约束 'Named'
2. 泛型接口
2.1 单类型参数的泛型接口
- 可以接受一个类型参数
interface GenericIdentityFn<T> {
//"(arg: T)"的T为参数arg的类型。 "): T"的T为返回值的类型
(arg: T): T;
}
const identity: GenericIdentityFn<number> = (arg: number): number => {
return arg;
};
console.log(identity(42)); //输出: 42
//下面的代码为熟悉后简写的
interface GenericIdentityFn<T> {
(arg: T): T;
}
const identity: GenericIdentityFn<number> = (arg) => arg;
console.log(identity(42)); //输出: 42
2.2 多类型参数的泛型接
- 可以定义一个接受多个类型参数的接口
interface Pair<K, V> {
key: K;
value: V;
}
const pair: Pair<string, number> = { key: "age", value: 30 };
console.log(pair); // 输出: { key: 'age', value: 30 }
2.3 泛型约束
- 可以为泛型添加约束,限制类型参数必须符合特定接口或类型
//定义一个接口 Lengthwise,包含一个 number 类型的 length 属性
interface Lengthwise {
length: number;
}
//定义一个泛型接口 GenericArray,要求泛型 T 必须扩展自 Lengthwise
interface GenericArray<T extends Lengthwise> {
items: T[]; //包含一个 T 类型的数组 items
getLength(): number; //包含一个返回 number 类型的 getLength 方法
}
//定义一个泛型类 ArrayContainer,实现 GenericArray 接口
class ArrayContainer<T extends Lengthwise> implements GenericArray<T> {
items: T[]; //定义 items 属性,类型为 T 类型的数组
//构造函数,接受一个 T 类型的数组并赋值给 items 属性
constructor(items: T[]) {
this.items = items;
}
//实现 getLength 方法,返回 items 数组的长度
getLength(): number {
return this.items.length;
}
}
//创建一个 ArrayContainer 实例,传入一个包含两个对象的数组,每个对象都有 length 属性
const container = new ArrayContainer([{ length: 5 }, { length: 10 }]);
//调用 getLength 方法并输出结果,输出: 2
console.log(container.getLength()); //输出: 2
2.4 默认类型参数
- 可以为泛型接口定义默认类型
interface GenericIdentityFn<T = string> {
(arg: T): T;
}
const myIdentity: GenericIdentityFn = (arg) => arg; // 默认 T 为 string
console.log(myIdentity("Hello")); // 输出: Hello
2.5 复杂类型
- 可以使用复杂类型作为泛型参数,比如嵌套的接口或类型
interface Box<T> {
content: T;
}
interface Item {
name: string;
}
const itemBox: Box<Item> = { content: { name: "Widget" } };
console.log(itemBox.content.name); // 输出: Widget
3. 泛型类
3.1 单类型参数的泛型类
- 这是最基本的泛型类定义,可以接受一个类型参数。
//定义一个泛型类 Box,接受类型参数 T
class Box<T> {
private value: T; //使用类型参数 T 定义一个私有属性
constructor(value: T) { //构造函数接收一个类型为 T 的参数
this.value = value;
}
getValue(): T { //方法返回类型为 T 的值
return this.value;
}
}
//创建一个 Box 实例,指定类型为 number
const numberBox = new Box<number>(42);
console.log(numberBox.getValue()); //输出: 42
//创建一个 Box 实例,指定类型为 string
const stringBox = new Box<string>("Hello");
console.log(stringBox.getValue()); //输出: Hello
3.2 多个类型参数的泛型类
- 可以定义一个接受多个类型参数的泛型类。
//定义一个泛型类 Pair,接受两个类型参数 K 和 V
class Pair<K, V> {
private key: K; //属性 key 的类型为 K
private value: V; //属性 value 的类型为 V
constructor(key: K, value: V) { //构造函数接收两个参数
this.key = key;
this.value = value;
}
getKey(): K { //返回类型为 K 的 key
return this.key;
}
getValue(): V { //返回类型为 V 的 value
return this.value;
}
}
//创建一个 Pair 实例,指定类型为 string 和 number
const pair = new Pair<string, number>("age", 30);
console.log(pair.getKey()); //输出: age
console.log(pair.getValue()); //输出: 30
3.3 泛型约束
- 可以为泛型添加约束,以限制类型参数的类型。
//定义一个接口,约束具有 length 属性的类型
interface Lengthwise {
length: number;
}
//定义一个泛型类,约束类型参数必须符合 Lengthwise 接口
class Container<T extends Lengthwise> {
private items: T[]; //使用类型参数 T 定义一个数组属性
constructor(items: T[]) {
this.items = items;
}
getLength(): number { //返回 items 数组的长度
return this.items.length;
}
}
//创建一个 Container 实例,数组项必须具有 length 属性
const stringContainer = new Container<string>(["Hello", "World"]);
console.log(stringContainer.getLength()); //输出: 2
//const numberContainer = new Container<number>([1, 2, 3]); //错误:number 没有 length 属性
3.4 默认类型参数
- 可以为泛型类定义默认类型参数。
//定义一个泛型类 Stack,默认类型为 number
class Stack<T = number> {
private items: T[] = []; //使用类型参数 T 定义一个数组
push(item: T): void { //方法接收类型为 T 的参数
this.items.push(item);
}
pop(): T | undefined { //返回类型为 T 的元素或 undefined
return this.items.pop();
}
}
//创建 Stack 实例,使用默认类型 number
const numberStack = new Stack(); //不指定类型,默认为 number
numberStack.push(1);
console.log(numberStack.pop()); //输出: 1
//创建 Stack 实例,指定类型为 string
const stringStack = new Stack<string>();
stringStack.push("Hello");
console.log(stringStack.pop()); //输出: Hello
3.5 复杂类型
- 可以使用复杂类型作为泛型参数,比如嵌套的接口或类型。
//定义一个接口 Item
interface Item {
id: number;
name: string;
}
//定义一个泛型类 Warehouse,接受 Item 类型的数组
class Warehouse<T extends Item> {
private items: T[] = []; //定义一个数组来存储项目
addItem(item: T): void { //添加项目
this.items.push(item);
}
getItems(): T[] { //返回项目列表
return this.items;
}
}
//创建一个 Warehouse 实例,存储 Item 类型
const warehouse = new Warehouse<Item>();
warehouse.addItem({ id: 1, name: "Item 1" });
console.log(warehouse.getItems()); //输出: [{ id: 1, name: 'Item 1' }]
三、工具类型
1. Partial<T>: 将对象类型的所有属性变为可选
Partial
将对象类型的所有属性变为可选。
interface User {
id: number;
name: string;
age: number;
}
//使用 Partial 将 User 的属性变为可选
const partialUser: Partial<User> = {
name: "Alice"
};
console.log(partialUser); //输出: { name: "Alice" }
//定义一个函数,接受一个 Partial<User> 类型的参数
function updateUser(user: Partial<User>) {
//假设我们有一个现有的用户对象
const existingUser: User = {
id: 1,
name: "Bob",
age: 25
};
//使用 Object.assign 更新现有用户对象。Object.assign 方法将传入的部分更新信息合并到现有用户对象中,返回更新后的用户对象。
const updatedUser = Object.assign({}, existingUser, user);
return updatedUser;
}
//调用 updateUser 函数,传入部分更新的用户信息。如果定义function的时候,接收参数不是Partial<T>,那么这个位置使用的时候只传一个age会报错
const updatedUser = updateUser({ age: 30 });
console.log(updatedUser); //输出: { id: 1, name: "Bob", age: 30 }
//调用 updateUser 函数,传入完整的用户信息
const fullyUpdatedUser = updateUser({ id: 2, name: "Charlie", age: 35 });
console.log(fullyUpdatedUser); //输出: { id: 2, name: "Charlie", age: 35 }
2. Required<T>: 将对象类型的所有属性变为必选
Required
将对象类型的所有属性变为必选。
//定义三个可选参数
interface User {
id?: number;
name?: string;
age?: number;
}
//使用 Required 将 User 的所有属性变为必选
const completeUser: Required<User> = {
id: 1,
name: "Alice",
age: 30
};
console.log(completeUser); //输出: { id: 1, name: "Alice", age: 30 }
//定义一个函数,接受一个 Required<User> 类型的参数
function printUser(user: Required<User>) {
console.log(`ID: ${user.id}, Name: ${user.name}, Age: ${user.age}`);
}
//调用 printUser 函数,传入完整的用户信息
printUser({ id: 2, name: "Bob", age: 25 }); //输出: ID: 2, Name: Bob, Age: 25
//调用 printUser 函数,传入不完整的用户信息会报错
//printUser({ id: 3, name: "Charlie" }); //错误: 类型 '{ id: number; name: string; }' 不满足约束 'Required<User>'
3. Readonly<T>: 将对象类型的所有属性变为可读
Readonly
将对象类型的所有属性变为可读。
interface User {
id: number;
name: string;
}
const readonlyUser: Readonly<User> = {
id: 1,
name: "Charlie"
};
//readonlyUser.name = "Dave"; //错误:无法分配到只读属性
console.log(readonlyUser); //输出: { id: 1, name: "Charlie" }
4. Record<K, T>: 创建一个对象类型
Record
创建一个对象类型,K
是键的类型,T
是值的类型。
//----------value为定义的接口类型----------
//定义一个接口,表示用户信息
interface User {
id: number;
name: string;
age: number;
}
//使用 Record 构造一个对象类型,其键为 string 类型,值为 User 类型
const userRecord: Record<string, User> = {
"user1": { id: 1, name: "Alice", age: 30 },
"user2": { id: 2, name: "Bob", age: 25 },
"user3": { id: 3, name: "Charlie", age: 35 }
};
//----------key为定义的接口类型----------
type Role = "admin" | "user" | "guest";
//创建一个以 Role 为键,string 为值的对象。 因为key已经确定是Role,那UserRoles里面的key和Role中一样,不能多,也不能少
const userRoles: Record<Role, string> = {
admin: "Admin",
user: "User",
guest: "Guest",
player: "Player" //报错:对象字面量只能指定已知属性,并且“player”不在类型“Record<Role, string>”中
};
console.log(userRoles); //输出: { admin: 'Admin', user: 'User', guest: 'Guest' }
5. Pick<T, K>: 从对象类型中选取一组属性
Pick
从对象类型中选取一组属性。
interface User {
id: number;
name: string;
age: number;
}
//使用 Pick 只选择 User 中的 id 和 name 属性
const pickedUser: Pick<User, "id" | "name"> = {
id: 1,
name: "Eve"
};
console.log(pickedUser); //输出: { id: 1, name: "Eve" }
6. Omit<T, K>: 从对象类型中排除指定的一组属性
Omit
从对象类型中排除指定的一组属性。
interface User {
id: number;
name: string;
age: number;
}
//使用 Omit 排除 age 属性
const omittedUser: Omit<User, "age"> = {
id: 2,
name: "Frank"
};
console.log(omittedUser); //输出: { id: 2, name: "Frank" }
7. Extract<T, U>:提取联合类型中并存的类型
Extract
提取联合类型中并存的类型。
type Status = "success" | "error" | "pending";
type ErrorStatus = "error" | "fatal";
//提取 Status 中同时存在于 ErrorStatus 中的类型
type CommonStatus = Extract<Status, ErrorStatus>; //"error"
8. Exclude<T, U>: 从一个联合类型中排除另一个联合类型的成员
Exclude
用于从一个联合类型中排除另一个联合类型的成员。
//定义一个联合类型 Status
type Status = "success" | "error" | "pending";
//定义一个联合类型 ExcludeStatus
type ExcludeStatus = "error" | "fatal";
//使用 Exclude 工具类型排除 Status 中属于 ExcludeStatus 的成员
type RemainingStatus = Exclude<Status, ExcludeStatus>; //"success" | "pending"
//示例函数,接受 RemainingStatus 类型的参数
function updateStatus(status: RemainingStatus) {
console.log(`Status updated to: ${status}`);
}
//调用 updateStatus 函数,传入 "success"
updateStatus("success"); //输出: Status updated to: success
//调用 updateStatus 函数,传入 "pending"
updateStatus("pending"); //输出: Status updated to: pending
//调用 updateStatus 函数,传入 "error" 会报错
//updateStatus("error"); //错误: 类型 '"error"' 不满足约束 'RemainingStatus'
//调用 updateStatus 函数,传入 "fatal" 会报错
//updateStatus("fatal"); //错误: 类型 '"fatal"' 不满足约束 'RemainingStatus'
9. NonNullable<T>: 排除类型中的null和undefined
NonNullable
排除类型中的null
和undefined
。
//定义一个联合类型,包含 string、number、null 和 undefined
type NullableType = string | number | null | undefined;
//使用 NonNullable 工具类型排除 NullableType 中的 null 和 undefined
type NonNullableType = NonNullable<NullableType>; //string | number
//示例函数,接受 NonNullableType 类型的参数
function printValue(value: NonNullableType) {
console.log(`Value: ${value}`);
}
//调用 printValue 函数,传入 string 类型的值
printValue("Hello"); //输出: Value: Hello
//调用 printValue 函数,传入 number 类型的值
printValue(42); //输出: Value: 42
//调用 printValue 函数,传入 null 会报错
//printValue(null); //错误: 类型 'null' 不满足约束 'NonNullableType'
//调用 printValue 函数,传入 undefined 会报错
//printValue(undefined); //错误: 类型 'undefined' 不满足约束 'NonNullableType'
10. ReturnType<T>: 获取函数的返回类型
ReturnType
获取函数的返回类型。
//定义一个函数 getUserId,返回一个 number 类型的值
function getUserId(): number {
return 123;
}
//使用 ReturnType 获取 getUserId 函数的返回类型
type UserId = ReturnType<typeof getUserId>; //number
//定义一个函数 getUserName,返回一个 string 类型的值
function getUserName(): string {
return "Alice";
}
//使用 ReturnType 获取 getUserName 函数的返回类型
type UserName = ReturnType<typeof getUserName>; //string
//示例函数,接受 UserId 类型的参数
function printUserId(id: UserId) {
console.log(`User ID: ${id}`);
}
//示例函数,接受 UserName 类型的参数
function printUserName(name: UserName) {
console.log(`User Name: ${name}`);
}
//调用 printUserId 函数,传入一个 number 类型的值
printUserId(123); //输出: User ID: 123
//调用 printUserName 函数,传入一个 string 类型的值
printUserName("Alice"); //输出: User Name: Alice
//调用 printUserId 函数,传入一个 string 类型的值会报错
//printUserId("123"); //错误: 类型 'string' 不满足约束 'number'
//调用 printUserName 函数,传入一个 number 类型的值会报错
//printUserName(123); //错误: 类型 'number' 不满足约束 'string'
11. InstanceType<T>: 获取构造函数的实例类型
InstanceType
获取构造函数的实例类型。
class User {
constructor(public name: string) {}
}
//获取 User 类的实例类型
type UserInstance = InstanceType<typeof User>;
const user: UserInstance = new User("Alice");
console.log(user); //输出: User { name: "Alice" }
12. ThisType<T>: 提供对上下文this的类型支持
ThisType
提供对上下文this
的类型支持(通常配合object
和--noImplicitThis
编译选项使用)。
interface MyObject {
count: number;
increment(): void;
}
const obj: MyObject & ThisType<MyObject> = {
count: 0,
increment() {
this.count++; //this 被推断为 MyObject
}
};
obj.increment();
console.log(obj.count); //输出: 1