1、定义
【泛型】指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。当想让函数/接口/类用于多种类型同时保证类型安全时,可以使用泛型。
2、泛型函数
function getValue<T>(value: T): T {
return value
}
getValue<number>(1)//或者id(1) ts具有类型推断
getValue<string>('a')
getValue<boolean>(false)
getValue(100)//类型推断为字面量类型100
【多个泛型参数的函数】一个函数可以定义多个泛型参数
function getArr<K, V>(value1: K, value2: V): [K, V] {
return [value1, value2]
}
const arr = getArr<number, string>(123, "哈哈哈")
console.log(arr) //[123,"哈哈哈"]
console.log(arr[0].toFixed(2), arr[1].split("")) //123.00 ["哈","哈","哈"]
3、泛型接口
【泛型接口】在定义接口时, 为接口中的属性或方法定义泛型类型在使用接口时, 再指定具体的泛型类型
interface IdFunc<T> {
id: (value: T) => T
ids: () => T[]
}
let obj1: IdFunc<number> = {
id(value) { return value },
ids() { return [1, 3] }
}
//数组是泛型接口实现的
const s1 = [1, 2, 3]
s1.forEach(item => { })
const s2 = ['a', 'b']
s2.forEach(item => { })
interface IBaseCRUD<T>{
data:Array<T>
add:(t:T)=>T
getUserId:(id:number)=>T
}
class User{
id?:number
name:string
age:number
constructor(name:string,age:number){
this.name=name
this.age=age
}
}
class UserCRUD implements IBaseCRUD<User>{
data:Array<User>=[]
add(user:User):User{
user.id=Date.now()+Math.random()
// 把用戶對象添加到data数组中
this.data.push(user)
return user
}
getUserId(id:number):any{
console.log("输出this.data",this.data)
console.log(this.data.find(user=>user.id===id));
return this.data.find(user=>user.id===id)
}
}
const userCRUD:UserCRUD=new UserCRUD()
userCRUD.add(new User("小明",20))
let {id}=userCRUD.add(new User("小方",30))
userCRUD.add(new User("布丁",25))
userCRUD.add(new User("小米",22))
console.log(userCRUD.data);
console.log(id);
typeof id =='number'?console.log("查找成功",userCRUD.getUserId(id)):console.log("查找失敗");
4、泛型类
【泛型类】定义一个类,类中的属性值的类型是不确定的,方法中的参数及返回值的类型也是不确定的。在定义类时, 为类中的属性或方法定义泛型类型。 在创建类的实例时, 再指定特定的泛型类型
class Persons<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
const ps1 = new Persons<string>("小美");
console.log(ps1.getValue());
const ps2 = new Persons<number[]>([1, 2, 3]);
console.log(ps2.getValue());
5、泛型约束
如果直接对一个泛型参数取length属性,会报错,因为这个泛型根本就不知道它有这个属性。定义一个接口,用来约束将来的某个类型中必须要有length这个属性
interface ILength {
length: number
}
//使用extends关键字为泛型添加约束
function getLength<T extends ILength>(value: T): number {
return value.length
}
console.log(getLength("I am a student")) //14
//keyof关键字获取对象所有键的集合
function getProp<T, k extends keyof T>(obj: T, key: k) {
return obj[key]
}
getProp({ name: "maidu", age: 11 }, 'name')
6、泛型工具类型
Partial-可选属性;readonly-只读属性;Pick<Type,keys> 获取某type中的某些属性;
Record<keys,Type>构造一个对象类型,属性键为keys2,属性类型为Type。
//Partial-可选属性
interface IProps {
id: string
title: string
children: number[]
}
type IPartialProps = Partial<IProps>
const a: IPartialProps = {} //可选属性0-2个
//readonly-只读属性
type IReadonlyProps = Readonly<IProps>
let a2: IReadonlyProps = { id: "1", title: "", children: [1] }
// a2.id = "3"//错误
//Pick<Type,keys> 获取某type中的某些属性
type IPickProps = Pick<IProps, 'id' | 'title'>
//Record<keys,Type>构造一个对象类型,属性键为keys2,属性类型为Type
type IRecord = Record<'a' | 'b' | 'c', string[]>
let a3: IRecord = { a: ['1'], b: ["we"], c: ["3"] }
7、索引签名类型
interface IAnyObj {
[key: string]: number
// [propName: string]: any;
}
//使用索引签名实现数组,通过索引访问数组
interface IMyArray<T> {
[index: number]: T
}
let arr: IMyArray<number> = [1, 22, 3]
arr[0]
8、映射类型
//类型映射只能使用在类型别名type中,不能在接口interface中使用
type PropsKeys = 'x' | 'y' | 'z'
type Types = { [Key in PropsKeys]: number } //等价于{x:number;y:number;z:number}
type PropsObj = { a: number; b: string; c: boolean }
type Type1 = { [key in keyof PropsObj]: number }
//Partial实现是基于映射类型
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
//索引查询类型
type Type2 = PropsObj['a']
type Type3 = PropsObj['a' | 'b']//同时查询多个索引类型
type Type4 = PropsObj[keyof PropsObj]//同时查询多个索引类型