typeScript-03接口与泛型

接口

  • 接口既可以在面向对象编程中表示为行为的抽象,也可以用来描述对象的形状
  • 我们用 interface 关键字来定义接口 在接口中可以用分号或者逗号分割每一项,也可以什么都不加
  • 定义的变量比接口少了一些属性是不允许的,多一些属性也是不允许的。
  • 赋值的时候,变量的形状必须和接口的形状保持一致
  • 接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀
//接口可以用来描述`对象的形状`
interface Speakable {
  speak(): void;
  readonly lng: string; //readonly表示只读属性 后续不可以更改
  name?: string; //?表示可选属性
}

let speakman: Speakable = {
  //   speak() {}, //少属性会报错
  name: "hello",
  lng: "en",
  age: 111, //多属性也会报错
};
  • 接口可以把一些类中共有的属性和方法抽象出来,可以用来约束实现此接口的类
  • 一个类可以实现多个接口,一个接口也可以被多个类实现
interface Speakable{
    speak():void
}
interface Eatable {
    eat ():void
}
//一个类可以实现多个接口
class Person implements Speakable,Eatable {
    speak(){}
    eat(){} 需要实现的接口包含eat方法 不实现会报错
}
定义任意属性
  • 如果我们在定义接口的时候无法预先知道有哪些属性的时候,可以使用 [propName:string]:any,propName 名字是任意的

    interface Person {
        id : number ,
        name :string ,
        [propName :string] :any
    }
    let p1 = {
        id: 1,
        name: "hello",
        age: 10,
      };
      //这个接口表示 必须要有 id 和 name 这两个字段 然后还可以新加其余的未知字段
    
接口的继承
  • 同样的使用 extends关键字

    interface Speakable {
      speak(): void;
    }
    interface SpeakChinese extends Speakable {
      speakChinese(): void;
    }
    class Person implements SpeakChinese {
      speak() {
        console.log("Person");
      }
      speakChinese() {
        console.log("speakChinese");
      }
    }
    
函数类型接口
  • 用接口定义函数类型

    interface discount {
        (price :number) :number
    }
    
    let cost:discount =function(price:number):number {
        return price *0.8
    }
    console.log(cost(20));
    
构造函数的类型接口
  • 使用特殊的new()关键字来描述类的构造函数类型

    class Animals {
        constructor (public name :string){}
    }
    interface WithNameClass {
        new(name:string):Animals
    }
    function createAnimals(clazz:WithNameClass,name :string){
        return new clazz(name)
    }
    let a = createAnimals(Animals , 'hello')
    console.log(a.name); //hello 
    
接口和类型别名的区别

在大多数的情况下使用接口类型和类型别名的效果等价,但是在某些特定的场景下这两者还是存在很大区别

  • 基础数据类型 与接口不同,类型别名还可以用于其他类型,如基本类型(原始值)、联合类型、元组

    type Name = string;
    
    // union
    type PartialPoint = PartialPointX | PartialPointY;
    
    // tuple
    type Data = [number, string];
    
    // dom
    let div = document.createElement("div");
    type B = typeof div;
    
  • 重复定义

    • 接口可以定义多次 会被自动合并为单个接口 类型别名不可以重复定义
    interface Point {
      x: number;
    }
    interface Point {
      y: number;
    }
    const point: Point = { x: 1, y: 2 };
    
  • 扩展 接口可以扩展类型别名,同理,类型别名也可以扩展接口。但是两者实现扩展的方式不同,接口的扩展就是继承,通过 extends 来实现。类型别名的扩展就是交叉类型,通过 & 来实现。

    // 接口扩展接口
    interface PointX {
      x: number;
    }
    
    interface Point extends PointX {
      y: number;
    }
    // ----
    // 类型别名扩展类型别名
    type PointX = {
      x: number;
    };
    
    type Point = PointX & {
      y: number;
    };
    // ----
    // 接口扩展类型别名
    type PointX = {
      x: number;
    };
    interface Point extends PointX {
      y: number;
    }
    // ----
    // 类型别名扩展接口
    interface PointX {
      x: number;
    }
    type Point = PointX & {
      y: number;
    };
    
  • 实现 这里有一个特殊情况 类无法实现定义了联合类型的类型别名

    type PartialPoint = { x: number } | { y: number };
    
    // A class can only implement an object type or
    // intersection of object types with statically known members.
    class SomePartialPoint implements PartialPoint {
      // Error
      x = 1;
      y = 2;
    }
    

泛型

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

  • 通俗理解:泛型就是解决 类 接口 方法的重用性、以及对不特定数据类型的支持

    改造前
    function createArray(length: number, value: any): any[] {
      let result = [];
      for (let i = 0; i < length; i++) {
        result[i] = value;
      }
      return result;
    }
    
    createArray(3, "x"); // ['x', 'x', 'x']
    
    泛型改造后
    function createArray<T>(length:number,value:T):Array<T>{
        let result :T[]=[]
        for (let i = 0; i < length; i++) {
            result[i] = value;
          }
        return result
    
    }
    createArray<string>(3, "x"); // ['x', 'x', 'x']
    
    
    • 我们可以使用<>的写法 然后再面传入一个变量 T 用来表示后续函数需要用到的类型 当我们真正去调用函数的时候再传入 T 的类型就可以解决很多预先无法确定类型相关的问题
多个类型参数
  • 如果我们需要有多个未知的类型占位 那么我们可以定义任何的字母来表示不同的类型参数

    function swap<T,U>(truple : [T,U]):[U,T]{
        return [truple[1] , truple[0]]
    }
    
    console.log(swap([7, "seven"]));
    
泛型约束

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

我们在泛型里面使用extends关键字代表的是泛型约束 需要和类的继承区分开

interface Lengthwise{
    length:number
}
function loggingIdentify<T extends Lengthwise>(arg:T):T{
    console.log(arg.length);
    
    return arg
}
泛型接口
  • 定义接口的时候也可以指定泛型
interface Cart<T> {
  list: T[];
}
let cart: Cart<{ name: string; price: number }> = {
  list: [{ name: "hello", price: 10 }],
};
console.log(cart.list[0].name, cart.list[0].price);

  • 我们定义了接口传入的类型 T 之后返回的对象数组里面 T 就是当时传入的参数类型
泛型类
class MyArray<T>{
    private list : T[]=[];
    add(value :T){
        this.list.push(value)
    }
    getMax():T{
        let result =this.list[0]
        for(let i=0; i <this.list.length ; i++ ){
            if(this.list[i]>result){
                result =this.list[i]
            }
        }
        return result
    }
}
let arr=new MyArray()
arr.add(1)
arr.add(2)
arr.add(3)
let ret=arr.getMax()
console.log(ret);
泛型类型别名
type Cart<T> = { list :T[]} | T[]
let c1 : Cart<string> ={list : [ '1']}
let c2: Cart<number> ={list : [ 2]}

使用技巧

  • keyof 可以用来取得一个对象接口的所有 key 值

  • typeof 获取一个变量的类型

interface Person {}
type PersonKey = keyof Person;
  • 使用 [] 操作符可以进行索引访问

    interface Person {
      name: string;
      age: number;
    }
    
    type x = Person["name"]; // x is string
    
  • 在定义的时候用 in 操作符去批量定义类型中的属性

    interface Person {
      name: string;
      age: number;
      gender: "male" | "female";
    }
    //批量把一个接口中的属性都变成可选的
    type PartPerson = {
      [Key in keyof Person]?: Person[Key];
    };
    
    let p1: PartPerson = {};
    
  • 在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用。

    type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
    
    • infer R 就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用。
  • 工具类

    • Exclude<T,U> 从 T 可分配给的类型中排除 U

      type Exclude<T, U> = T extends U ? never : T;
      
      type E = Exclude<string | number, string>;
      let e: E = 10;
      
    • Extract<T,U> 从 T 可分配给的类型中提取 U

    • NonNullable从 T 中排除 nullundefined

    • ReturnTypeinfer 最早出现在此 PR 中,表示在 extends 条件语句中待推断的类型变量

    • Parameters该工具类型主要是获取函数类型的参数类型

    • PartialPartial 可以将传入的属性由非可选变为可选

    • RequiredRequired 可以将传入的属性中的可选项变为必选项,这里用了 -? 修饰符来实现。

    • ReadonlyReadonly 通过为传入的属性每一项都加上 readonly 修饰符来实现。

    • Pick<T,K> Pick 能够帮助我们从传入的属性中摘取某些返回

    • Record<K,T> 构造一个类型,该类型具有一组属性 K,每个属性的类型为 T。可用于将一个类型的属性映射为另一个类型。Record 后面的泛型就是对象键和值的类型。

    • Omit<K,T> 基于已经声明的类型进行属性剔除获得新类型

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值