泛型
指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
实例
// 功能: 根据指定的数量 count 和数据 value, 创建一个包含 count 个 value 的数组 不用泛型的话,这个函数可能是下面这样:
function createArray(value: any, count: number): any[] {
const arr: any[] = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr1 = createArray(11, 3)
const arr2 = createArray('aa', 3)
console.log(arr1); // [11, 11, 11]
console.log(arr2); // ["aa","aa","aa"]
// console.log(arr2[0].toFixed(), arr1[0].split('')) // error,却不提示
console.log('--------------------------');
// 使用泛型
function createArray2<T>(value: T, count: number) {
const arr: Array<T> = []
for (let index = 0; index < count; index++) {
arr.push(value)
}
return arr
}
const arr3 = createArray2<number>(11, 3)
console.log(arr3);
console.log(arr3[0].toFixed())
// console.log(arr3[0].split('')) // error
const arr4 = createArray2<string>('aa', 3)
console.log(arr4);
console.log(arr4[0].split(''))
// console.log(arr4[0].toFixed()) // error
多个泛型参数的函数
function swap<K, V>(a: K, b: V): [K, V] {
return [a, b]
}
const result = swap<string, number>('abc', 1234)
console.log(result[0].length, result[1].toFixed())
console.log(result); // ["abc", 1234]
泛型接口
// 在定义接口时, 为接口中的属性或方法定义泛型类型
// 在使用接口时, 再指定具体的泛型类型
interface IBase<T>{
data: T[], // 详细信息
add: (t:T)=> void, // 增加信息(除了id)
getById: (id: number)=>T // 通过id查询信息
}
class User{
id?: number;
name : string;
age: number;
constructor(name:string, age:number){
this.name = name
this.age = age
}
}
class UserInfo implements IBase<User>{
data: User[] = []
add(user:User):void {
user = { ...user, id: Date.now() }
this.data.push(user)
console.log('保存user', user.id)
}
getById(id: number): User{ // 由于开启了严格模式
let user = new User('0',0) // 代表没有查询到的默认值
let rst = this.data.find(item => item.id === id)
return rst === undefined ? user : rst
// 由于 find 方法未满足条件函数会返回undefined,在严格模式下下面的代码会报错,可以选择在tsconfig.json文件中关闭严格模式
// return this.data.find(item => item.id === id)
}
}
const user = new UserInfo()
user.add(new User('tom', 12))
user.add(new User('tom2', 13))
console.log(user.getById(Date.now()))
console.log(user.data)
泛型类
在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
class GenericNumber<T> {
// 在 TypeScript 2.7 中引入了一个名为 --strictPropertyInitialization 的新标志。该标志执行检查以确保类的每个实例属性在构造函数主体中或由属性初始化器初始化。
// 严格模式下下面两行代码会报错:
// zeroValue: T
// add: (x: T, y: T) => T
// 可以使用明确赋值断言修饰符(definite assignment assertion)
// 说白了就是在定义的时候它没有具体的初始值,但是我确信它在运行期间一定是有值的,这样就规避了这个类型检查。
zeroValue!: T
add!: (x: T, y: T) => T
}
let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function (x, y) {
return x + y
}
let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function (x, y) {
return x + y
}
console.log(myGenericString.add(myGenericString.zeroValue, 'test'))
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))
或者这样改写上面的例子:
class GenericNumber1<T> {
// to get rid of error, you can define constructor
// which takes [zeroValue] and [add] as arguments
constructor(public zeroValue: T, public add: (x: T, y: T) => T) {
this.zeroValue = zeroValue;
this.add = add;
}
}
const zeroValue = 0;
let myGenericNumber1 = new GenericNumber1<number>(zeroValue, (a, b) => a + b);
const res = myGenericNumber1.add(40, 2)
console.log(res) // 42
泛型约束
假如直接对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性。
// 约束接口
interface Lengthwise {
length: number;
}
// 指定泛型约束
function fn2<T extends Lengthwise>(x: T): void {
console.log(x.length)
}
fn2('abcd') // 4