3

简介

此为高级类型的第一部分,学习交叉类型(Intersection Types)、联合类型(Union Types)以及在使用以上类型时如何区分具体类型。

交叉类型(Intersection Types)

交叉类型把几个类型的成员合并,形成一个拥有这几个类型所有成员的新类型。从字面上理解,可能会误认为是把取出几个类型交叉的(即交集)成员。

交叉类型的使用场景:Mixins、不适合用类来定义。

我觉得,交叉类型和Mixins有一点区别:交叉类型只是一个类型声明,用于类型约束;Mixins会给类增加成员,new对象时,对象会包含增加的成员属性。

我们看一看示例:

改自官方示例,官方示例有2个小问题:会提示不存在属性“hasOwnProperty”;另外,es6下prototype是不可以枚举的,无法通过枚举合并类属性。
  interface IAnyObject {
    [prop: string]: any
  }

  function extend<First extends IAnyObject, Second extends IAnyObject>(first: First, second: Second): First & Second {
    const result: Partial<First & Second> = {};
    for (const prop in first) {
      if (first.hasOwnProperty(prop)) {
        (result as First)[prop] = first[prop];
      }
    }
    for (const prop in second) {
      if (second.hasOwnProperty(prop)) {
        (result as Second)[prop] = second[prop];
      }
    }
    return result as First & Second;
  }

  interface IPerson {
    name: string,
    age: number
  }

  interface IOrdered {
    serialNo: number,
    getSerialNo(): number
  }

  const personA: IPerson = {
    name: 'Jim',
    age: 20
  }

  const orderOne: IOrdered = {
    serialNo: 1,
    getSerialNo: function () { return this.serialNo }
  }

  const personAOrdered = extend<IPerson, IOrdered>(personA, orderOne);
  console.log(personAOrdered.getSerialNo());
  • 其中First & Second就是交叉类型的写法。
  • 通过扩展,可以合并两传入对象的成员属性,增强现有对象的能力。

联合类型(Union Types)

联合类型与交叉类型类似,都可以拥有多个类型的能力,区别是:联合类型一次只能一种类型;而交叉类型每次都是多个类型的合并类型。

联合类型在实际项目中,使用场景比交叉类型广泛得多。

  • 字符串填充:
  function padLeft(value: string, padding: number | string) {
    if (typeof padding === "number") {
      return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
      return padding + value;
    }
    return value;
  }

  padLeft("Hello world", 4);
  • 再举个常见的例子,常量的联合类型,五分制考试打分:
  type Scores = 1 | 2 | 3 | 4 | 5;

  function rating(score: Scores) {
    console.log(`Set score ${score}`);
  }

  rating(3);
  // error
  // rating(6); 

枚举也可以实现,但是这样更简洁。

  • 还可以用于可为null的参数:
function f(sn: string | null): string {
    if (typeof sn === null) {
        return 'default';
    } else {
        return sn;
    }
}
  • 还可以使用类型别名定义联合类型(上面场景2中用过):
type Method = 'get' | 'post';

类型保护和类型区分

使用联合类型时,我们是无法知道编译时的具体类型的,所以在运行时必须要确定类型。所以代码中需要做类型保护和类型区分。

为什么叫类型保护?因为在运行时,必须知道明确类型,否则可能会访问一个不存在的成员属性,导致程序出错。

  • 自定义类型保护

      // 先判断是否存在成员属性
      if(pet.swim) {
          
      }
  • in操作符

      // 先判断是否存在成员属性
      if('swim' in pet) {
          
      }
  • typeof操作符

    function f(sn: string | number): string {
        if (typeof sn === 'number') {
            return sn.toString();
        } else {
            return sn;
        }
    }
  • instanceof操作符

    // 判断实例原型
    if(pet instanceof Fish) {
          
    }
  • 类型断言

    function f(sn: string | null): string {
        if (sn === null) {
            return "default";
        } else {
            return sn;
        }
    }

联合类型应用极广泛,非常好用。


xie_zhichao
1.1k 声望274 粉丝

Web全栈开发者。