typeScript进阶

247 阅读3分钟

范型(Generics)

范型是指定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

function createArray(length:number,value:any):Array<any>{
    let result = [];
    for(let i = 0;i<length;i++){
        result[i] = value;
    }
    return result;
}

它并没有准确的定义返回值的类型: Array 允许数组的每一项都为任意类型。但是我们预期的是,数组中每一项都应该是输入的 value 的类型。

function createArray<T>(length:number,value:T):Array<T>{
    let result = [];
    for(let i = 0;i<length;i++){
        result[i] = value;
    }
    return result;
}
createArray(3,'x') // ['x','x','x']

上例中,我们在函数名后添加了 ,其中 T 用来指代任意输入的类型,在后面的输入 value: T 和输出 Array 中即可使用了。 接着在调用的时候,可以指定它具体的类型为 string。

多个类型参数

定义范型的时候,可以一次定义多个类型参数。

function wrap<T, U>(tuple:[T,U]):[U,T]{
    return [tuple[1],tuple[0]]
}

wrap([1,'name']) // ['name',1]

范型约束

在函数内部使用范型变量的时候,由于事先不知道它是那种类型,所以不能随意的操作它的属性和方法。

    function log<T>(x:T):T{
        console.log(x.length)
        return x
    }
    // Property 'length' does not exist on type 'T'

上例中,泛型 T 不一定包含属性 length,所以编译的时候报错了。

这时,我们可以对泛型进行约束,只允许这个函数传入那些包含 length 属性的变量。这就是泛型约束:

    interface WithLength{
        length:number
    }
    
    function log<T extends WithLength>(x:T):T{
        console.log(x.length)
        return x
    }
    
    log(3) // Argument of type '3' is not assignable to parameter of type 'WithLength'.

上例中,我们使用了 extends 约束了泛型 T 必须符合接口 WithLength 的形状,也就是必须包含 length 属性。

此时如果调用 log 的时候,传入的 arg 不包含 length,那么在编译阶段就会报错了。

范型接口

使用接口可以描述一个函数的形状,当然也可以使用含有泛型的接口来定义函数的形状。

    interface CreateArrayFunc {
        <T>(length:number,value:T):Array<T>
    }
    
    let createArray : CreateArrayFunc;
    
    createArray = function <T>(length:number,value:T):Array<T>{
        let result = [];
        for(let i = 0;i<length;i++){
            result[i] = value;
        }
        return result;
    }

进一步,我们可以把泛型参数提前到接口名上:

    interface CreateArrayFunc <T>{
        (length:number,value:T):Array<T>
    }
    
    let createArray : CreateArrayFunc<any>;
    
    createArray = function <T>(length:number,value:T):Array<T>{
        let result = [];
        for(let i = 0;i<length;i++){
            result[i] = value;
        }
        return result;
    }
    

注意,此时在使用泛型接口的时候,需要定义泛型的类型。

范型类

与泛型接口类似,泛型也可以用于类的类型定义中:

class Person<T>{
    age:T
    add:(x:T,y:T)=> T
}

let people  = new Person<number>();

people.age = 12

类中未赋值会出错,设置 "strictPropertyInitialization": false, 关闭提示

声明文件

TypeScript 查漏补缺

interface 和 type

interface (接口) 用来对类型进行描述, type 是给类型起个名字, 其实作用是类似的, 但是有些区别。

相同点

  1. 都可以描述一个对象和一个函数

       interface User {
           name: string,
           age: number
       }
    
       type User = {
           name: string,
           age: number
       }  
         
       interface Fn {
           (age: number): number
       }
         
       type Fn = (age: number) => number;
    
  2. 都可以扩展

       interface Name { 
         name: string; 
       }
       interface User extends Name { 
         age: number; 
       }
       
       type Name = { 
         name: string; 
       }
       type User = Name & { age: number  };
           
       type Name = { 
         name: string; 
       }
       interface User extends Name { 
         age: number; 
       }
           
       interface Name { 
         name: string; 
       }
       type User = Name & { 
         age: number; 
       }
    

不同点

  1. type 可以声明 基本类型别名, 联合类型, 元组等。

       type age = string | number;
    
  2. interface 可以声明合并

       interface User{
           name: string
       }
    
       interface User{
           age: number
       }
         
       const user: User = {
         name: 'bu',
         age: 12
     	}
    

Record

Record是很好用的工具类型, 它会将一个类型的所有属性值都映射到另一个类型上并创造一个新的类型。源码 :

       type Record<K extends keyof any, T> = {
          [P in K]: T;
      }; 

demo

type Type = 'teacher' | 'worker' | 'leader'; // key

interface Person{   // value
    name: string
}
          
const user: Record<Type, Person> = {
    teacher: {
        name: 'buzhan'
    },
    worker: {
        name: 'sss'
    },
    leader: {
        name: 'sss'
    }
}
   

通用类型

构造泛型类型

  interface Desc<T,P> {
      name: string,
      age: P,
      type:T
  }

  enum Person {
      man = 'man',
      woman = 'woman',
  }

  enum Animal {
      dog = 'dog',
      cat = 'cat',
  }
          
  const person: Desc<Person, string> = {
      name: 'bu',
      age: '12',
      type: Person.man
  };

  const dog: Desc<Animal, number> = {
      name: 'dog',
      age: 2,
      type: Animal.dog
  }

可选Partial

interface Person {
    name: string,
    age: number
}
const person: Partial<Person> = {
    name: 'buzhan'
}

Required 必传

  interface Person {
      name: string,
      age: number
  }
  const person: Required<Person> = {
      name: 'buzhan',
      age: 23
  }
      

Readonly 只读

  interface Person {
      name: string,
      age: number
  }
  const person: Readonly<Person> = {
      name: 'buzhan',
      age: 23
  }
  person.name = 'sss'; // error
         

Omit<T,k> 忽略

Omit<T,k> 将k在T类型上删除

  interface Person {
      name: string,
      age: number,
      sex: string
  }
  const person: Omit<Person,'age'|'name'> = {
      age: 23, // 报错
      sex: 'man'
  }
          

挑选 Pick<T,k>

类型中只能包含挑选的类型。

  interface Person {
    name: string,
    age: number,
    sex: string
}
const person: Pick<Person,'age'|'name'> = {
    name: 'buzhan',
    age: 23,
    sex: 'sss' // 报错
}