函数泛型
引入需求
如果我们不使用泛型
我们会这样子去实现:
function numberArray(a1:number, a2:number):number[]{return [a1, a2]
}
function stringArray(a1:string, a2:string):string[]{return [a1, a2]
}
但是如果我还需要别的类型参数呢,比如bool值等,不断像上面这样创建类似的代码会让代码非常丑且冗余
所以这里我们需要用到泛型,简而言之,泛型就是把类型当做参数
和Java中的泛型原理是一样的!
那么我们使用泛型来实现上面这个需求就很简单了
function array<T>(a:T, b:T):T[]{return [a, b]
}
只需要在函数名后面加上尖括号就可以使用泛型了
使用这个方法也非常简单,给T赋值就行了:
console.log(array(1,2))
console.log(array<number>(1,2))
(这里指定T为number类型)
可是通常情况下,我的函数参数类型不一定只有一种,该怎么办呢
很简单,我们可以定义多种泛型
如:
function array<T,K>(a:T, b:K):(T|K)[]{return [a, b]
}console.log(array(1,"2"))
console.log(array<number, string>(1,"2"))
泛型约束
开发过程中,我们可能遇到这种情况,在泛型函数中,我们需要调用参数的.length方法,但在定义函数时,泛型具体是什么类型还没有被指定,无法确认选择的类型是否真的有length方法,这个时候我们得想办法对泛型进行约束。
就拿需要调用参数的.length方法为例,我们知道,只有string和数组类型才能调用,所以我们可以通过extend去限制泛型
function array<T extends string|any[]>(a:T, b:T):T[]{console.log(a.length)return [a, b]
}console.log(array("12334","2"))
console.log(array(["1", "33"],["2"]))
接口泛型
普通的接口函数一般是定义一些要写的变量和函数要求
interface Fun {(name: string):string
}let fun:Fun = function (name:string):string{return ""
}
但是接口里的函数也可能是泛型1函数
interface ArrType<T> {(a1: T,a2:T):T[]
}let fun:ArrType<string> = function (a1:string, a2:string):string[]{return [a1, a2]
}
(只需要把泛型类型定义在接口后面的尖括号中,其实也可以写在内部的函数前面,像这样⬇,但是这样就只是针对于该函数的写法,并不推荐使用)
interface ArrType {<T>(a1: T,a2:T):T[]
}let fun:ArrType = function<string> (a1:string, a2:string):string[]{return [a1, a2]
}
除了接口中的方法可以使用泛型定义,属性当然也可以的
interface Info<T> {like: T
}let zhangsan: Info<string> = {like: "羽毛球"
}
let lisi: Info<string[]> = {like: ["羽毛球"]
}
这里同样可以加上泛型约束
interface Info<T extends string|string[]> {like: T
}let zhangsan: Info<string> = {like: "羽毛球"
}
let lisi: Info<string[]> = {like: ["羽毛球"]
}
弹药注意,这里不要和接口继承搞混了,我们知道,接口也是可以继承的,它和泛型约束相似但不一样。
interface People {name?: string
}
interface Info<T extends string|string[]> extends People{like: T
}let zhangsan: Info<string> = {like: "羽毛球"
}
let lisi: Info<string[]> = {like: ["羽毛球"]
}
keyof
这里我们又有一个需求,我们需要传入一个key来调取某个对象的key属性,我们该如何去做。
我们可能会将key作为参数传进去,再获取对象的key属性:
interface Info {name: stringage: number
}let zhangsan:Info = {name:"zhangsan",age: 21
}function getInfoValue(info:Info, key: string):void{// 这个时候,这里会报错,原因是因为传入的这个key,有可能不是Info的属性console.log(info[key])
}getInfoValue(zhangsan, "name")
(!typescript的类型检查会帮你避免很多问题哦)
当key属性并不在Info中时,会报错!
上面这种方法没办法解决,我们可以使用keyof来实现。
interface Info {name: stringage: number
}let zhangsan:Info = {name:"zhangsan",age: 21
}function getInfoValue(info:Info, key: keyof Info):void{console.log(info[key])
}getInfoValue(zhangsan, "name")
getInfoValue(zhangsan, "age")
getInfoValue(zhangsan, "age1") // 不满足keyof的校验,会报错
(key : keyof Info)限定了传入的key必须是Info里面的属性!
再结合前面学习的泛型,这里我们可以进行优化:
interface Info {name: stringage: number
}let zhangsan:Info = {name:"zhangsan",age: 21
}function getInfoValue<T extends keyof Info>(info:Info, key: T):Info[T]{return info[key]
}getInfoValue(zhangsan, "name")
getInfoValue(zhangsan, "age")