泛型的基本知识点梳理(泛型类型参数化,接口,类,约束,参数约束,映射类型等)

文章目录

  • 一 类型参数化
  • 二 泛型接口
  • 三 泛型类
  • 四 泛型约束
  • 五 泛型参数使用约束
  • 六 映射类型


一 类型参数化

我们来提一个需求:封装一个函数,传入一个参数,并且返回这个参数

  • 如果我们是TypeScript的思维方式,要考虑这个参数和返回值的类型需要一致:
function foo(arg: number): number{
    return arg
}
  • 注意:上面的代码虽然实现了,但是不适用于其他类型,比如string,boolean等类型
//虽然any是可以的,但是定义为any的时候,我们其实已经丢失了类型信息
function foo(arg: any): any{
    return arg
}
  • 我们需要在这里使用一种特性的变量 - 类型变量(type variable),它作用于类型,而不是值:
  1. 基本使用
function foo<Type>(arg: Type): Type {
    return arg
}

//完整写法-通过<类型>的方式将类型传递给函数
//res1的类型是number
const res1 = foo<number>(123)
//res2的类型是string
const res2 = foo<string>("kobe")
//res3的类型是{name: string}
const res3 = foo<{name: string}>({name: "jack"})
  1. 类型推导:自动推导出我们传入变量的类型
function foo<Type>(arg: Type): Type {
    return arg
}

//省略写法
//res4经过类型推导后是123(字面量类型),const推导的会更具体
const res4 = foo(123)
//res5经过类型推导后是number
let res5 = foo(123)
  1. useState的泛型实现
function useState<Type>(initialState: Type): [Type, (newState: Type) => void] {
    let state = initialState
    function setState(newState: Type) {
        state = newState
    }
    return [state, setState]
}

//省略写法
const [count,setCount] = useState(123)
const [message, setMessage] = useState("kobe")
//此时传入一个空字符串,ts类型推导不正确,所以最好指定一下类型
const [banners, setBanners] = useState<any[]>([])
  1. 传入多个类型
function foo<Type, Element>(arg1: Type, arg2: Element) {
    return arg
}

foo(123, 321)
foo(123, "abc")
  1. 常用缩写
    • T: Type的缩写,类型
    • K、V: key和value的缩写, 键值对
    • E:Element的缩写, 元素
    • O:Object的缩写,对象

二 泛型接口

定义接口时也可以使用泛型

  1. 基本使用
//定义接口
interface personType<Type> {
    name: string
    age: Type
    height: Type
}

//personType的类型是number
const Person:personType<number> = {
    name: "jack",
    age: 19,
    height: 189
}
//personType的类型是string
const Person:personType<string> = {
    name: "jack",
    age: "19",
    height: "189"
}
  1. 接口类型有默认值
//定义接口,定义接口类型默认是number类型
interface personType<Type = number> {
    name: string
    age: Type
    height: Type
}

//personType的类型是默认类型:number
const Person:personType = {
    name: "jack",
    age: 19,
    height: 189
}

三 泛型类

class Point<Type = number> {
    x: Type
    y: Typr
    constructor(x: Type, y: Type) {
        this.x = x
        this.y = y
    }
}

const p1 = new Point(10, 20)
//使用默认类型number类型,x为number类型
console.log(p1.x)

const p2 = new Point("10", "20")
//自动进行类型推导为string类型,所以x为string类型
console.log(p2.x)

四 泛型约束

  • 有时候我们希望传入的类型有某些共性,但是这些共性可能不是在同一种类型中:
    • 比如string和array都是有length的,或者某些对象也是会有length属性的;
    • 那么只要是拥有length的属性都可以作为我们的参数类型。
interface ILength {
    length: number
}
//方法一:可以实现获取字符串,数组,对象的长度,但length1,2,3丢失了类型,都是ILength类型 
function getLength(arg: ILenght) {
    return arg.length
}
const length1 = getLength("abc")
const length2 = getLength(["aa", "bb", "cc"])
const length2 = getLength({ length: 100 })


//方法二:获取传入的内容,这个内容必须有length属性,也可以有其他属性,但是必须至少有这个成员。
//Type相当于是一个变量,用于记录本次调用的类型,所以在整个函数的执行周期中,一直保留着参数info1,2,3的类型
function getInfo<Type extends ILength>(args: Type): Type {
    return args
}
const info1 = getInfo("abc")
const info2 = getInfo(["aa", "bb", "cc"])
const info3 = getInfo({ length: 100 })

//错误,因为参数都没有length属性
const info4 = getInfo(12345)
const info5 = getInfo({})

五 泛型参数使用约束

在泛型约束中使用类型参数: 你可以声明一个类型参数,这个类型参数被其他类型参数约束

  • 举个栗子:我们希望获取一个对象给定属性名的值
    • 我们需要确保我们不会获取 obj 上不存在的属性;
    • 所以我们在两个类型之间建立一个约束;
//K extends keyof O得到的是obj所有key的联合类型: "name"|"age"|"height"
function getProperty<O, K extends keyof O>(obj: O, key: K) {
    return obj[key]
}

const info = {
    name: "jack",
    age: 18,
    height: 1.89
}

const name = getProperty(info, "name") //正确
const address = getProperty(info, "address") //错误,因为address不是info的其中一个key

六 映射类型

有的时候,一个类型需要基于另外一个类型,但是你又不想拷贝一份,这个时候可以考虑使用映射类型。

  • 大部分内置的工具都是通过映射类型来实现的;

  • 大多数类型体操的题目也是通过映射类型完成的;

  • 映射类型建立在索引签名的语法上:
    • 映射类型,就是使用了 PropertyKeys 联合类型的泛型;
    • 其中 PropertyKeys 多是通过 keyof 创建,然后循环遍历键名创建一个类型;
  1. 基本使用
//TS提供了映射类型:函数
//Type = Iperson
//keyof = "name"|"age"
type MapPerson<Type> {
    //索引类型依次进行使用:
    [property in keyof Type]: Type[property]
                     //name : string
                      //age : number
}
interface IPerson {
    name: string
    age: number
}

type NewPerson = MapPerson<IPerson>

泛型的基本知识点梳理(泛型类型参数化,接口,类,约束,参数约束,映射类型等)_第1张图片

  1. 映射修饰符使用

在使用映射类型时,有两个额外的修饰符可能会用到:

  • 一个是 readonly,用于设置属性只读

  • 一个是 ? ,用于设置属性可选

type MapPerson<Type> {
    //设置属性可选和属性只读
    readonly [property in keyof Type]?: Type[property]
                     //readonly name ?: string
                      //readonly age ?: number
}
interface IPerson {
    name: string
    age: number
}

type IPersonOptional = MapPerson<IPerson>

泛型的基本知识点梳理(泛型类型参数化,接口,类,约束,参数约束,映射类型等)_第2张图片

  1. 修饰符符号

你可以通过前缀 - 或者 + 删除或者添加这些修饰符,如果没有写前缀,相当于使用了 + 前缀。

type MapPerson<Type> {
    //设置属性可选和属性只读
    +readonly [property in keyof Type]+?: Type[property]
                     //readonly name ?: string
                      //readonly age ?: number
    -readonly [property in keyof Type]-?: Type[property]
                                 //name : string
                                  //age : number
}
interface IPerson {
    name: string
    age: number
}

type IPersonOptional = MapPerson<IPerson>

泛型的基本知识点梳理(泛型类型参数化,接口,类,约束,参数约束,映射类型等)_第3张图片

泛型的基本知识点梳理(泛型类型参数化,接口,类,约束,参数约束,映射类型等)_第4张图片

你可能感兴趣的:(TS,javascript,开发语言,ecmascript)