• TypeScript高级类型


    交叉类型

    将多个类型合并成一个类型,取两个类型的并集。与继承的区别是,继承可以有自己的属性,而交叉没有。

    interface DogInterface {
       run():void
    }
    interface CatInterface {
       jump():void
    }
    let pet: DogInterface & CatInterface = { // 看上去和接口多继承很像,但有一点区别。继承可以有自己的属性,交叉不行。
       run(){},
       jump(){},
    };

    联合类型

    声明的类型并不确定,可以是多个类型中的一个。

    let a: number | string = "a"; // 类型限定
    let b: "a" | "b" | "c"; // 限定取值
    let c: 1 | 2 | 3 | "v"; // 限定取值

    可区分的类型保护:

    // 现在有两种形状,area函数用来计算每种形状的面积。
    interface Square{
       kind: "square";
       size: number;
    }
    interface Rectangle{
       kind: "rectangle",
        number,
       height: number,
    }
    type Shape = Square | Rectangle;
    function area(s: Shape) {
       switch (s.kind) {
          case "square":
             return s.size * s.size; // 此区块内,确保只有size属性
          case "rectangle":
             return s.height * s.width;
       }
    }
    console.log(area({kind:"square",size:10})); // 100
    // 现在要添加一个形状:圆形。需要定义接口Circle、为Shape添加联合类型Circle,然后为area函数内增加一个case。但是,如果我们忘了修改area函数,会发生什么?
    interface Circle{
       kind: "circle",
       r: number,
    }
    type Shape = Square | Rectangle | Circle;
    console.log(area({kind:"circle",r:10})); // undefined,这里并不报错,并不符合我们的预期。我们希望bug能够及时暴露出来,增加程序的稳定性。
    做如下改动:
    function area(s: Shape) {
       switch (s.kind) {
          case "square":
             return s.size * s.size;
          case "rectangle":
             return s.height * s.width;
          case "circle":
             return Math.PI * s.r;
          default:
             return ((e: any)=>{throw new Error(`没有定义 ${s} 的面积计算方式`)})(s) // 这一步很重要,一定要在这里抛出异常
       }
    }

    索引类型

    当我们使用不存在的索引时,会返回undefined,没有约束(如下代码)。因此我们需要有对索引的约束。

    let obj = {
       a: 1,
       b: 2,
       c: 3,
    };
    function getValue(obj: any,keys: string[]){
       return keys.map(key => obj[key]);
    }
    console.log(getValue(obj,["a","b"]));
    console.log(getValue(obj,["c","f"])); // 会发现,'f'对应的输出是undefined,没有约束,需要用到索引类型

    下面使用索引类型:

    function getValue<T,K extends keyof T>(obj: T, keys: K[]): T[K][] { // T[k][]表示,返回值必须是obj中的值组成的列表
       return keys.map(key => obj[key]); // 此时keys中的元素只能是obj中的键
    }
    console.log(getValue(obj,["a","b"]));
    console.log(getValue(obj,["c","f"])); // 这时就会报错,有了约束 'f' is not in "a" | "b" | "c"

    我们来解释一下:

    这里会用到两个操作符,查询操作符 keyof T 和 访问操作符 T[k](看下面示例)。<T, K extends keyof T> 用到了泛型约束,表示K所约束的参数的值只能是T所约束参数数据中的“键”。

    // keyof T
    interface Obj{
       a: number;
       b: string;
    }
    let key: keyof Obj; // 此时key表示 'a' | 'b'
     
    // T[k]
    let value: Obj['a'] // number

    映射类型

    可以从旧的类型生成新的类型。比如,将接口中的所有成员变成只读、可选。

    TS内置了很多映射类型。

    interface Obj{
       a: string;
       b: number;
       c: boolean;
    }
    // 将Obj接口中每个成员变成只读属性,生成一个新的接口。
    type ReadonlyObj = Readonly<Obj>;  // Readonly是TS内置的映射类型,下同
    // Readonly实现原理,利用了索引类型的操作方法
    type Readonly<T> = {
       readonly [P in keyof T]: T[P];
    }

    再如:

    // 将所有属性变成可选
    type PartialObj = Partial<Obj>;
    // Partial实现原理
    type Partial<T> = {
       [P in keyof T]?: T[P];
    }
    
    // 获取原类型的子集
    type PickObj = Pick<Obj,'a'|'b'>;
    // 等同于
    interface PickObj {
       a: string,
       b: number
    }
    
    // 将原类型当做新类型的成员
    type RecordObj = Record<'x'|'y',Obj>;
    // 等同于
    interface RecordObj {
       x: Obj,
       y: Obj, 
    }

    沙发

    条件类型

    条件类型指由表达式所决定的类型。条件类型使类型具有了不唯一性,增加了语言的灵活性。

    例如:

    T extends U ? X : Y   表示如果类型T可以被赋值给类型U,name结果就赋予X类型,否则赋予Y类型

    再如:

    type TypeName<T> =
       T extends string ? string :
       T extends number ? number :
       T extends boolean ? boolean :
       T extends undefined ? undefined :
       T extends Function ? Function :
       object;
    
    type T1 = TypeName<string>;   // T1为字符串类型
    type T2 = TypeName<string[]>; // T2为object类型
    type T3 = TypeName<Function>; // T3为function类型
    type T4 = TypeName<string | string[]>; // T4为 string和object的联合类型

     可以用来做什么?

    (A | B) extends U ? X : Y
    解析为(A extends U ? X : Y) | (B extends U ? X : Y)

    可以利用这一特性做类型的过滤,例如:

    type Diff<T,U> = T extends U ? never : T;
    
    type T5 = Diff< 'a'|'b'|'c', 'a'|'e' >; // 作用是过滤掉第一个参数中的'a' 。T5为 'b' | 'c'联合类型
    解析过程:
     Diff<'a', 'a'|'e'> | Diff<'b', 'a'|'e'> | Diff<'c', 'a'|'e'>
     never | 'b' | 'c'
     'b' | 'c'

    TS内置的条件类型:

    Exclude<T, U>  // 从T中剔除可以赋值给U的类型,相当于上面例子中的Diff
    Extract<T, U>  // 提取T中可以赋值给U的类型。
    NonNullable<T>  // 从T中剔除null和undefined。
    ReturnType<T>   // 获取函数返回值类型。
    InstanceType<T>  // 获取构造函数类型的实例类型。
  • 相关阅读:
    [转载][mysql]mysql字符集干货
    [mysql]修改表段默认值
    微信支付之h5方式(非微信内置浏览器中支付)
    阿里云 ECS 安全组
    Memcached cas 陷阱
    Memcached 分布式集群
    nginx 配置多个主机
    static类型的变量
    全局变量和局部变量
    nginx 负载均衡(默认算法)
  • 原文地址:https://www.cnblogs.com/V587Chinese/p/11478492.html
Copyright © 2020-2023  润新知