泛型是用来创建可重用的组件的,是一种传递类型的方式,一个组件可以支持多种类型的数据
- 常用的泛型标识符
- T(Type):代表Type,在定义泛型时通常用作第一个类型变量名。
- K(Key):表示对象中的键类型
- V(Value):表示对象中的值类型
- E(Element):表示元素类型
1. 泛型函数
2. 泛型接口
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
3. 泛型类
interface GenericInterface<U> {
value: U
getIdentity: () => U
}
class IdentityClass<T> implements GenericInterface<T> {
value: T
constructor(value: T) {
this.value = value
}
getIdentity(): T {
return this.value
}
}
const myNumberClass = new IdentityClass<Number>(68);
console.log(myNumberClass.getIdentity()); // 68
const myStringClass = new IdentityClass<string>("Semlinker!");
console.log(myStringClass.getIdentity()); // Semlinker!
实例化 myNumberClass 的调用过程:
- 在实例化 IdentityClass 对象时,传入 Number 类型和构造函数参数值 68;
- 之后在 IdentityClass 类中,类型变量 T 的值变成 Number 类型;
- IdentityClass 类实现了 GenericInterface,而此时 T 表示 Number 类型,因此等价于该类实现了 GenericInterface 接口;
- 而对于 GenericInterface 接口来说,类型变量 U 也变成了 Number。
4. 泛型约束
4.1. 确保属性存在:通过 extends 关键字添加泛型约束
希望类型变量对应的类型上存在 length 属性,让 T 具有 length 属性
实现:让类型变量 extends 一个含有 length 属性的接口
interface Length {
length: number;
}
function identity<T extends Length>(arg: T): T {
console.log(arg.length); // 可以获取length属性
return arg;
}
当我们使用不含有 length 属性的对象作为参数调用 identity 函数时,TypeScript 会提示相关的错误信息:
identity(68); // Error
// Argument of type '68' is not assignable to parameter of type 'Length'.(2345)
还可以使用 , 号来分隔多种约束类型,比如:<T extends Length, Type2, Type3>
4.2. 检查对象上的键是否存在:keyof
keyof 操作符用于获取某种类型的所有键,返回类型是联合类型。
interface Person {
name: string;
age: number;
}
type PersonKeys = keyof Person; // "name" | "age"
function getValueByKey<T extends object, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = {
name: 'Bob',
age: 25,
};
const name = getValueByKey(person, 'name'); // name 的类型是 string
const age = getValueByKey(person, 'age'); // age 的类型是 number
const gender = getValueByKey(person, 'gender'); // 编译报错,gender 不是 person 中的属性
在 中,extends 关键字表示限制 K 的取值只能是 T 类型中已有的属性名。也就是说,只有 K 取值为 T 类型中已有的属性名才符合类型约束
5. 类型推导与默认参数
5.1. 类型推导
function id<T>(arg: T): T {
return arg;
}
id<string>("lucifer"); // 这是ok的,也是最完整的写法
id("lucifer"); // 基于类型推导,我们可以这样简写
5.2. 默认参数
interface A<T = string> {
name: T;
}
const strA: A = { name: "Semlinker" };
const numB: A<number> = { name: 101 };
泛型参数的默认类型遵循以下规则:
- 有默认类型的类型参数被认为是可选的。
- 必选的类型参数不能在可选的类型参数后。
- 如果类型参数有约束,类型参数的默认类型必须满足这个约束。
- 当指定类型实参时,你只需要指定必选类型参数的类型实参。 未指定的类型参数会被解析为它们的默认类型。
- 如果指定了默认类型,且类型推断无法选择一个候选类型,那么将使用默认类型作为推断结果。
- 一个被现有类或接口合并的类或者接口的声明可以为现有类型参数引入默认类型。
- 一个被现有类或接口合并的类或者接口的声明可以引入新的类型参数,只要它指定了默认类型。
6. 泛型工具
6.1. Partial:将类型T的所有属性转换为可选属性
interface Person {
name: string;
age: number;
}
const partialPerson: Partial<Person> = {
name: 'John',
};
// partialPerson的类型为 Partial<Person>,即 { name?: string; age?: number; }
6.2. Record<K, T>:创建一个类型,其中键为类型K中的每个属性,并将它们映射到类型T。
interface PageInfo {
title: string;
}
type Page = "home" | "about" | "contact";
const x: Record<Page, PageInfo> = {
home: { title: "home" },
about: { title: "about" },
contact: { title: "contact" },
};
6.3. Pick<T, K>:从类型T中选择指定属性K。
interface Person {
name: string;
age: number;
address: string;
}
type PersonNameAndAddress = Pick<Person, 'name' | 'address'>;
// PersonNameAndAddress的类型为 { name: string; address: string; }
6.4. Omit<T, K>:从类型T中排除指定属性K。
interface Person {
name: string;
age: number;
address: string;
}
type PersonWithoutAge = Omit<Person, 'age'>;
// PersonWithoutAge的类型为 { name: string; address: string; }
6.5. ReturnType:获取函数类型T的返回类型。
function greet(name: string): string {
return `Hello, ${name}!`;
}
type GreetReturnType = ReturnType<typeof greet>;
// GreetReturnType的类型为 string