泛型
1. 泛型的使用范围
泛型可以用于函数、对象、类...
2. 指定函数参数类型
2.1 单个泛型
根据长度和内容创建一个数组
// 根据参数不同,处理结果不同
// 入参和返回值有映射关系
const getArray = <T>(times: number, val: T): T[] => {
let result = [];
for (let i = 0; i < times; i++) {
result.push(val);
}
return result;
};
getArray(3, "a"); // ['abc','abc','abc'] 当我使用的时候可以确定类型
2.2 多个泛型
function swap<T, K>(tuple: [T, K]): [K, T] {
return [tuple[1], tuple[0]]
}
console.log(swap(['a','b']))
3. 函数标注的方式
元祖交换
3.1 函数类型本身
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
// 测试
let swapResult = swap(["jw", true]);
let r1 = swapResult[0];
let r2 = swapResult[1];
3.2 类型别名
type ISwap = <T, U>(tuple: [T, U]) => [U, T];
const swap: ISwap = (tuple) => {
return [tuple[1], tuple[0]];
};
let swapResult = swap(["jw", true]);
let r1 = swapResult[0];
let r2 = swapResult[1];
可以使用类型别名,但是类型别名不能被继承和实现。一般联合类型可以使用类型别名来声明
3.3 使用接口定义函数类型
interface ISwap {
<T, U>(tuple: [T, U]): [U, T];
}
const swap: ISwap = (tuple) => {
return [tuple[1], tuple[0]];
};
let swapResult = swap(["jw", true]);
let r1 = swapResult[0];
能使用interface尽量使用interface
4. 泛型接口使用
interface ISum<T> { // 这里的T是使用接口的时候传入
<U>(a: T, b: T): U // 这里的U是调用函数的时候传入
}
let sum: ISum<number> = (a:number, b:number) => {
return 3 as any
}
4.1 给函数参数定义类型
type ICallback = <T>(arg: T) => void
let forEach = <T>(arr: T[], callback: ICallback) => {
for (let i = 0; i< arr.length; i++) {
callback(arr[i])
}
}
forEach(['a', 'b', 'c', 1], (item) => {
console.log(item)
})
4.2 给函数定义类型-使用接口的时候确定的类型
// T泛型放在前面,表示接口使用的时候需要传递类型过来
type ICallback<T> = (arg: T) => void
// 使用类型ICallback时就需要把泛型传递下去ICallback<T>
type IForEach = <T>(arr: T[], callback: ICallback<T>) => void
// 后面的arr,callback会在函数调用时进行类型推导
let forEach: IForEach = (arr, callback) => {
for (let i = 0; i< arr.length; i++) {
callback(arr[i]) // callback 没有执行, 所以无法推导arr[i] = T
}
}
// 函数调用的时候进行类型推导
forEach(['a', 'b', 'c', 1], (item) => {
console.log(item)
})
4.3 给函数定义类型-在调用函数的时候确定了类型
type ICallback = <T>(arg: T) => void
type IForEach = <T>(arr: T[], callback: ICallback) => void
// 后面的arr,callback会在函数调用时进行类型推导
let forEach: IForEach = (arr, callback) => {
for (let i = 0; i< arr.length; i++) {
callback(arr[i]) // callback 没有执行, 所以无法推导arr[i] = T
}
}
// 函数调用的时候进行类型推导
forEach(['a', 'b', 'c', 1], (item) => {
console.log(item)
})
5. 默认泛型
// 泛型可以指定默认值 默认泛型 给泛型增加了默认值
type Union<T = string> = T | number; // 场景就是如果用户不传入类型 我也希望有默认值
type t1 = Union;
type t2 = Union<boolean>;
6. 泛型约束
6.1 约束传入的泛型类型
A extends B A是B的子类型
// 此方法 传入number -》 number
// 传入 string -》 string
// boolean 这是错误的
function handle<T extends number | string>(val: T): T {
return val;
}
let r1 = handle(123);
let r2 = handle("abc");
// let r3 = handle(true);
interface IWithLength {
length: number;
}
// abc extends IWithLength 把字符串幻想成一个基于对象扩展的类型
function getLen<T extends IWithLength>(val: T) {
return val.length;
}
getLen((a: number, b: string) => {});
可以通过下面的方法验证字符串字面量的类型是IWithLength的子类
interface IWithLength {
length: number;
}
type test = '11' extends IWithLength ? '22': never
6.2 约束索引的签名
// 第二个参数只能是第一个参数中的key
function getVal<T extends object, U extends keyof T>(obj: T, key: U) {
return obj[key];
}
getVal({ a: 1, b: 2, c: 3 }, "c");
6.3 返回泛型中指定属性
const getVal = <T,K extends keyof T>(obj:T,key:K) : T[K]=>{
return obj[key];
}
const sum = <T extends number>(a: T, b: T): T => {
// 强制类型转换
return (a + b) as T
}
let r = sum<number>(1, 2);
7. 对象中可以使用泛型
常见的就是描述接口的返回值
// code:200, data:?, message
interface ApiResponse<T = any> {
code: number;
data: T; // 坑位
message?: string;
}
interface LoginRes {
token: string;
}
function toLogin(): ApiResponse<LoginRes> {
return {
code: 200,
data: {
token: "Bearer token",
},
};
}
let r = toLogin();
r.data.token;
8. 类中可以使用泛型
// 求列表中的最大值 调用的时候可以限制传入的类型
class MyList<T extends number | string> {
private arr: T[] = [];
add(val: T) {
this.arr.push(val);
}
getMax(): T {
let arr = this.arr;
let max = arr[0];
for (let i = 0; i < arr.length; i++) {
let cur = arr[i];
cur > max ? (max = cur) : void null;
}
return max;
}
}
let list = new MyList();
list.add(1);
list.add(100);