:::warning
泛型是指在定义函数、接口或类的时候,
不预先指定具体的类型,而在使用的时候再指定类型的一种特性
:::
在我们声明一个函数的时候,并且这个函数的入参可以是任意类型的时候,我们很难去做到确定返回的参数类型。
function echo(arg:any):any{
return arg
}
// const result: any
// 这里的result返回的类型还是any.
const result = echo(123)
如何使用泛型来确定类型
// 我们可以使用<>符号,里面定义一个占位符,这个占位符可以是任意字段
function echo<T>(arg:T):T{
return arg
}
// 这个时候他的类型不再是any了
// const result: 123
const result = echo(123)
// const result: true
const result = echo(true)
function swap<T, U>(tuple: [T,U]): [U, T]{
return [tuple[1], tuple[0]]
}
// const result: [number, string]
const result = swap(['string',123])
总结
:::warning
其实泛型就相当于一个占位符或者说是一个变量,
在使用的时候,我们可以把定义好的类型像参数一样传入,
然后它原封不动的给我们输出回来
:::
泛型约束 extends
在函数内部使用泛型变量的时候,由于事先不清楚它是哪种类型,所以不能随意的操作它的属性或方法。
:::warning 所谓的泛型约束,其实就是规定传入的类型必须按照我们规定的写法来传入 :::
例如
// 因为泛型T,不一定包含属性length
function echoWithArr<T>(arg: T): T{
console.log(arg.length) // 会报错 类型“T”上不存在属性“length”
return arg
}
// 改造, 但是这种写法只能传数组
function echoWithArr<T>(arg: T[]): T[]{
console.log(arg.length) // 这样就没问题了
return arg
}
const arrs = echoWithArr([1,2,3])
// 所以我们可以对泛型进行一个约束,让他只能传入有length的参数
interface IWithLength {
length: number
}
// 我们可以< 变量 extends 类型 >
function echoWithLength<T extends IWithLength>(arg: T): T{
console.log(arg.length)
return arg
}
// 只要有length属性,都可以,所以这也是鸭子类型的概念
const str = echoWithLength('str')
const obj = echoWithLength({ length:10, width:10})
const arr2 = echoWithLength([1,2,3])
// 报错,类型“number”的参数不能赋给类型“IWithLength”的参数
const num = echoWithLength(13)
泛型与类和接口
当我们在类里面定义一些方法,并且这些方法只有某些类型才有。那么我们在使用该类的方法是有可能会报错,并且ts在编译过程中无法获取错误
:::warning
像下面例子,我们分别添加了数值和字符串,但是我们都调用了toFixed方法,因为toFixed是属于number的方法。
所以str调用一定会报错,但是在下面的例子中,ts无法检测报错,只有在运行时才能发现。
:::
例如
class Queue{
private data = [];
push(item){
return this.data.push(item)
}
pop(){
return this.data.shift()
}
}
const queue = new Queue()
queue.push(1);
queue.push('str')
console.log(queue.pop().toFixed())
console.log(queue.pop().toFixed())
改造一下
class Queue<T>{
private data = [];
push(item:T){
return this.data.push(item)
}
pop():T{
return this.data.shift()
}
}
const queue = new Queue<number>()
queue.push(1);
// 类型“string”的参数不能赋给类型“number”的参数。
queue.push('str') // 报错
console.log(queue.pop().toFixed())
console.log(queue.pop().toFixed())
接口和泛型
:::warning 我们的接口interface也可以使用泛型变得更灵活 :::
// 我们这种写法可以自定义传值,更加灵活
interface KeyPair<T, U> {
key: T;
value: U;
}
let kp1: KeyPair<number, string> = { key:1, value:'str'}
let kp2: KeyPair<string, number> = { key:'str', value:1}
// 我们之前定义的数字类型数组
let arr:number[] = [1,2,3]
// 如果使用泛型和ts内置的interface-number
let arrTwo: Array<number> = [1,2,3]