TypeScript学习8:泛型

函数泛型

引入需求

如果我们不使用泛型

我们会这样子去实现:

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: string
    age: 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: string
    age: 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: string
    age: 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")

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值