TypeScript 入门 10 - 泛型

159 阅读3分钟

泛型

泛型:指的是类型参数化,即将原来某种具体的类型进行参数化。

  • 可以给泛型定义若干个类型参数,并在调用时给泛型传入明确的类型参数。
  • 目的: 有效约束类型成员之间的关系,如 函数参数和返回值、类或者接口成员和方法之间的关系
泛型类型参数
  • 通过尖括号<>语法给函数定义一个泛型参数P,并指定param参数的类型为P

    function reflect<P>(param: P){
    	return param
    }
    
  • 通过 <> 语法指定了类型入参

    const reflectStr = reflect<string>('abc')	// str的类型是string
    const reflectNum = reflect<number>(1)	// num的类型是 number
    

    如果调用泛型函数时受泛型约束的参数有传值,泛型参数的入参可以从参数的类型中进行推断,而无需再显示指定类型(可缺省)

    const reflectStr2 = reflect('abc')	// str的类型是string
    
  • 泛型不仅可以约束函数整个参数的类型,还可以约束参数属性、成员的类型

    function reflectArray<P>(param: P[]){
    	return param
    }
    const reflectArr = reflectArray([1, '1'])	//reflectArr是 (string | number)[]
    

    注意:函数的泛型入参必须和参数、参数成员建立有效的约束关系才有实际意义

  • 可以给函数定义任何个数的泛型入参

    function reflect<P, Q>(p1: P, p2: Q): [P, Q]{
    	return [p1, p2]
    }
    
泛型类
  • 在类的定义中,可以使用泛型来约束构造函数、属性、方法的类型

    class Memory<S>{
    	store: S
    	constructor(store: S){
    		this.store = store
    	}
    	set(store: S){
    		this.store = store
    	}
    	get(){
    		return this.store
    	}
    }
    
    // 使用
    const num = new Memory<number>(1)	// <number> 可缺省
    
    const str = new Memory('name')		// <string>缺省
    
  • 对 React 开发者而言,组件也支持泛型

    function GenericCom<P>(props: {prop1: string}){
    	return ...
    }
    <GenericCom<{name: string }> prop1 = "1"/>
    
泛型类型
  • 可以使用Array<类型>的语法来定义数组类型,Array本身就是一种类型。

  • 在TS中,类型本身就可以被定义为拥有不明确的类型参数的泛型。并且可以接收明确类型作为入参,从而衍生出更具体的类型。

    const reflectFn: <P>(params: P) => P = reflect	
    
    • 可以把 reflectFn的类型注解提取为一个能被复用的类型别名或者接口:

      // 提取为类型别名
      type ReflectFunction = <P>(params: P) => P
      // 提取为接口
      interface IReflectFunction {
      	<P>(params: P): P
      }
      
      const reflectFn2: ReflectFunction = reflect
      const reflectFn3: IReflectFunction = reflect
      
    • 定义了两个可以接收参数P的泛型类型

      type ReflectFunction<P> = (params: P) => P
      
      interface IReflectFunction<P>{
      	(params: P): P
      }
      
      const reflectFn4: ReflectFunction<string> = reflect
      const reflectFn5: IReflectFunction<number> = reflect
      
    • 可以使用一些类型操作符进行运算表达,使得泛型可以根据入参的类型衍生出各异的类型

      type StrOrNumArray<E> = E extends string | number ? E[] : E
      type StrArray = StrOrNumArray<string>	//类型是 string[]
      type NumArray = StrOrNumArray<number>	//类型是 number[]
      type NoArray = StrOrNumArray<boolean>	//类型是 boolean
      
    • 分配条件类型:在条件类型判断的情况下,如果入参的是联合类型,则会被拆解成一个个独立的(原子)类型(成员)进行类型运算。

    • 利用泛型,我们可以抽象封装出很多有用、复制的类型约束

      interface ReduxModel<State> {
      	state: State,
      	reducers: {
      		[action: string]: (state: State, action: any) => State
      	}
      }
      

      枚举类型不支持泛型

泛型约束
  • 可以把泛型入参限定在一个相对更明确的集合内,以便对入参进行约束

    • 可以使用 “泛型入参名 extends 类型”语法达到这个目的

      function reflect<P extends number|string|boolean>(param: P): P{
      	return param
      }
      
    • 可以把接口泛型入参约束在特定的范围内

      interface ReduxModel<State extends {id: number, name: string}>{
      	state: State
      }
      
    • 可以在多个不同的泛型入参之间设置约束关系

      interface ObjSetter {
      	<O extends {}, K extends keyof O, V extends O[K]>(obj: O, key: K, value: V): V
      }
      const setValueOfObj: ObjSetter = (obj, key, value) => (obj[key] = value)
      
    • 可以给泛型入参指定默认值(默认类型),且语法和指定函数默认参数完全一致

      interface ReduxModel2<State = {id: number, name: string}>{
      	state: State
      }
      
    • 泛型入参的约束与默认值可以组合使用

      interface ReduxModelMixed<State extends {} = {id: number, name: string}>{
      	state: State
      }