软件工程中,我们不仅要创建一致的定义良好的
API
,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时提供了十分灵活的功能。 泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持
在代码编译阶段,我们在写代码不确定这个A
变量的类型,虽然不确定,但想对不确定的A
变量进行相同的处理。
这里的“类型”:不仅指数据类型,还可以指变量的类,还可以指变量的接口等等。
在代码运行阶段,这个变量肯定是已知的某个类型,这个时候,所谓的泛型才变成了固定的类型。
而泛型变量呢,是我们用来暂时取代变量A
的类型的变量。
泛型变量的别称,类型变量。
类型不确定的情况,都是在函数传参的时候发生的,叫做泛型函数。
相应的,还有泛型类、泛型接口
1. 常见泛型变量
常见泛型变量代表的意思:
- T(Type): 表示一个
TypeScript
类型 - K(Key):表示对象中的键类型
- V(Value):表示对象中的值类型
- E(Element):表示元素类型
这是约定俗成的用法。
2. 泛型函数
// 要求传入的参数类型和返回类型一致
// T 表示泛型,具体什么类型是调用这个方法的时候决定的
function getData<T>(value: T): T {
return value;
}
getData({ name: 'konsoue' })
// 使用函数是,可以提前告知函数,泛型变量的值
getData<number>(123);
getData<string>('123');
现在我们添加了一个泛型变量 T 到函数中,这个变量允许我们获取,用户在使用这个函数时参数的类型。
这个函数里我们将 T 作为参数的类型和返回值的类型。
function test<T, D>(val1: T, val2: D): D {
return val2;
}
test(123, 22);
test<number, string>(123, '22');
// 交换函数
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([1, '2'])
3. 泛型类
class MinClass<T> {
public list: Array<T> = [];
add(item: T): void {
this.list.push(item);
}
remove(item: T): void {
this.list.splice(this.list.indexOf(item), 1);
}
}
let test = new MinClass();
test.add(2);
test.add({ name: 'konsoue' });
test.add('5');
let num = new MinClass<number>();
num.add(55);
num.add(1)
明白了,上面的可以这样做,就能理解下面这段代码了
- 把类作为参数来约束数据传入类型 ```typescript class User { userName: string | undefined; password: string | undefined; }
class MysqlDb { add(user: User): boolean { return true; } } let user = new User(); user.userName = ‘张三’; user.password = ‘123456’;
let db = new MysqlDb(); db.add(user);
- 把类当作参数的泛型类
```typescript
// 操作数据库的泛型类
class MysqlDb<T> {
add(info: T): boolean {
return true;
}
}
// 想给 user 表添加数据
let db = new MysqlDb<User>();
db.add(user);
let db2 = new MysqlDb<ArticleCate>();
db2.add(article);
4. 泛型接口
// 普通函数接口
interface ConfigFn {
(value1: string, value2: string): [string, string];
}
const setData: ConfigFn = function (value1, value2) {
return [value1, value2];
}
// 泛型接口写法一
interface ConfigFn {
<T>(value1: T, value2: T): [T, T];
}
const setData: ConfigFn = function (value1, value2) {
return [value1, value2];
}
// 泛型接口写法二
interface ConfigFn<T> {
(value1: T, value2: T): [T, T];
}
const setData: ConfigFn<string> = function (value1, value2) {
return [value1, value2];
}
5. 约束泛型
我们可以对泛型进行一些约束,从而约束传进来的数据的类型。
interface IWithLength {
length: number;
}
// 限制传入的泛型类型具备 length 属性
function echoWithLength<T extends IWithLength>(arg: T): T {
console.log(arg.length);
return arg;
}
const str = echoWithLength('str');
const obj = echoWithLength({ length: 10 })
const arr = echoWithLength([1, 2, 3])
6. 泛型工具类型
为了方便开发者, TypeScript
内置了一些常用的工具类型,比如 Partial
、 Required
、 ReadOnly
、 Record
和 ReturnType
等。
6.1 关键字
typeof 和 type
typeof 操作符可以用来获取一个变量声明或对象的类型。
interface IUserProps {
vType: 'userProps';
name: string;
email: string;
}
function isUserProps(x: any): x is IUserProps {
return x.vType === 'userProps';
}
class UserProps {
name: string;
email: string;
vType: 'userProps';
constructor(name: string, email: string) {
this.name = name;
this.email = email;
this.vType = 'userProps';
}
}
const userProps: IUserProps = new UserProps('konsoue', 'xx@qq.com')
type copyIuserProps = typeof userProps // copyIuserProps 就是 IUserProps
const obj: copyIuserProps = {} // 报错。类型“{}”缺少类型“IUserProps”中的以下属性: vType, name, email
keyof
用来获取一个对象中的所有
key
值
interface Person {
name: string;
age: number;
}
type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
type K3 = keyof { [x: string]: Person }; // string | number
const a: K1 = { name: 'John', age: 15 } // x
const a: K1 = 'name'; // √
in
用来遍历枚举类型
type Keys = "a" | "b" | "c"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any, c: any }
extends
有时候我们定义的泛型不想过于灵活或者说想继承某些类等,可以通过 extends 关键字添加泛型约束
interface ILengthwise {
length: number;
}
function loggingIdentity<T extends ILengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity(3); // Error, number doesn't have a .length property
// 需要传入符合约束类型的值,必须包含必须的属性
loggingIdentity({length: 10, value: 3});
infer
在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用。
type ReturnType<T> = T extends (
...args: any[]
) => infer R ? R : any;
以上代码中 infer R
就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用。
6.2 Partial
Partial<T>
的作用是将某个类型里的属性全部变为可选项。
源码中 Partial 的定义
type Partial<T> = {
[P in keyof T]?: T[P];
};
使用例子
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}
const todo1 = {
title: "organize desk",
description: "clear clutter",
};
const todo2 = updateTodo(todo1, {
description: "throw out trash",
});
/*
fieldsToUpdate 的类型为 Partial<Todo>, 即
{
title?: string | undefined;
description?: string | undefined;
}
*/
6.3 Required
Required<T>
将传入的属性变为必选项,,源码如下
type Required<T> = {
[P in keyof T]-?: T[P]
};
-?
:减掉可选+?
:加上可选。+ 可省略