上一篇 《TypeScript系列:续篇 - 对象类型(含数组、元数组、函数)》 中有提及对象类型中的"泛型"使用,本篇将详细展开...
TypeScript 引入泛型是为了增强代码的复用性、健壮性。
ts
interface Box {
contents: any;
}
interface Box<Type> {
contents: Type;
}
泛型允许开发者编写一次代码,适用于多种数据类型,减少冗余。
其在编译时提供类型检查,提前发现错误,提高代码的健壮性。
定义
泛型(Generics)本质上是一种参数化类型。
在定义时不指定具体数据类型 ,用一个或多个占位符(通常用字母「如 T
、U
、V
」)来表示类型参数;而在使用时指定具体的数据类型。
ts
function identity<T>(arg: T): T {
return arg;
}
上述函数返回值的类型 与参数类型相关;可以接收任何类型的参数,并返回相同类型的值。
<T>
指类型参数,可以将其理解为类型声明需要的变量,在调用时传入具体的参数类型。
- 调用时提供具体类型
ts
identity<string>('ligang')
- 省略不写,TypeScript 通常可以在泛型调用中推断出预期的类型参数。
ts
identity('ligang')
泛型写法
泛型可以在:函数、接口、类和别名 中定义。
泛型函数
定义一个泛型函数identity
,它接受一个类型参数 T
,并返回这个类型的值
ts
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("ligang"); // 当然,这里的string可省略,Typescript会自行推断
泛型接口
定义了一个泛型接口 Lengthwise<T>
,具有 length
属性和 value
属性,其中 length
是一个数字,而 value
是一个类型为T
的值。
泛型函数 classArray
,接受一个类型为 Lengthwise<T>
的参数,并返回一个类型为 Lengthwise<T>
的值。
ts
interface Lengthwise<T> {
length: number;
value: T;
}
function classArray<T>(arg: Lengthwise<T>): Lengthwise<T> {
return arg;
}
classArray({ value: 'TypeScript', length: 10 });
泛型类
定义泛型类 Box<T>
,其构造函数接受一个类型参数 T
和一个参数 content
,并将这个 content
赋值给类的私有属性 _content
。
ts
class Box<T> {
private _content: T;
constructor(content: T) {
this._content = content;
}
get content(): T {
return this._content;
}
}
const box = new Box<number>(123);
🐾 泛型别名
传入一个类型,得到这个类型与 undefined
和 null
的一个联合类型。
ts
type Nullable<T> = T | undefined | null;
let a: Nullable<string> = 'ligang';
let b: Nullable<string> = null;
let c: Nullable<string>;
经典案例
泛型与别名的结合实现表格响应体的类型:
1️⃣ 响应结构体
ts
interface Res<T> {
status: 'success' | 'error'; // 状态码
data: T; // 返回数据
}
2️⃣ table 数据格式
ts
interface TableData<T> {
pageNumber: number; // 页码
pageSize: number; // 每页条数
total: number; // 总条数
data: T[]; // 表格数据
}
3️⃣ 结合:响应表格结构体
ts
type ResTableData<T> = Res<TableData<T>>;
- 这里的
T
为表格数据类型
4️⃣ 调用示例
ts
// 表格数据为 string
const tableData: ResTableData<string> = {
status: 'success',
data: {
pageNumber: 1,
pageSize: 10,
total: 100,
data: ['1', '2'],
},
};
// 表格数据为 city
interface city {
code: number;
name: string;
}
const res: ResTableData<city> = {
status: 'success',
data: {
pageNumber: 1,
pageSize: 10,
total: 100,
data: [
{ code: 10010, name: '北京' },
{ code: 10011, name: '上海' },
],
},
};
常见的泛型表示
类型 | 说明 | 示例 |
---|---|---|
Array<T> |
T 可指定任何类型 |
let myAry: Array<number> = [1, 2, 3] |
Map<K, V> |
K 、V 分别是键和值的类型参数,可指定任何类型 |
let myMap: Map<string, number> = new Map() |
Set<T> |
T 可指定任何类型 |
let mySet: Set<number> = new Set([1, 2, 3]) |
Promise<T> |
T 可指定任何类型 |
let tableData = (): Promise<ResTableData<city>> => fetch('...').then((res) => res.json()) |
ts
interface Array<Type> {
length: number;
pop(): Type | undefined;
push(...items: Type[]): number;
...
}
泛型约束
如果需要限制泛型的使用范围,可以使用泛型约束!
ts
<Type extends Constraint>
🌿 示例:提供一个获取对象属性的方法
ts
function getProp<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
getProp({ name: 'ligang', age: '34' }, 'name'); // 正确
getProp({ name: 'ligang', age: '34' }, 'address'); // 错误,address 对象中不存在
具体报错:类型""address""的参数不能赋给类型""name" | "age""的参数
keyof
keyof
是 TypeScript 中的关键字,用于从一个对象类型中提取所有键(属性名)的联合类型。
ts
interface Person {
name: string;
age: number;
}
type PersonKeys = keyof Person; // 'name' | 'age'
✊ 综上,解释 getProp()
,只能获取对象既存的属性!
除了使用 keyof
方式,也可以自定义约束条件~~
🍃 示例:限制必须有 length
属性
函数的作用:比较两个实现了 Len
接口的值的长度,并返回长度较大的那个值。
ts
interface Len {
length: number;
}
function compareLen<T extends Len, U extends Len>(a: T, b: U) {
return a.length - b.length ? a : b;
}
compareLen([1, 2, 3], 'ab');
总结
泛型不仅提高了代码的可重用性和灵活性,同时也保证了类型的安全性。掌握好泛型的使用对于提升 TypeScript 编程技能至关重要!