淘先锋技术网

首页 1 2 3 4 5 6 7

高级类型

所谓高级类型,就是 ts 为了保障语言的灵活性所引入的语言特性。这些特性有助于我们应对复杂多变的开发场景。

交叉类型

交叉类型是将多个类型合并为一个类型。它包含了所需的所有类型的特性。 (合集)
我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。

interface DogInterface {
  run(): void;
}
interface CatInterface {
  jump(): void;
}
let pet: DogInterface & CatInterface = { run() {}, jump() {} };

联合类型

联合类型表示一个值可以是几种类型之一。
如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员

let a: number | string = "string"; // ok!
a = 1; // ok!
  • 字面量联合类型

    字面量类型:ts 提供的一个准确变量

    let b: 1 | "2" | true = 1; // ok!
    b = false; // error!
    
  • 对象联合类型

    class Dog implements DogInterface {
      run() {}
      eat() {}
    }
    class Cat implements CatInterface {
      jump() {}
      eat() {}
    }
    enum Master {
      Boy,
      Girl,
    }
    function getPet(master: Master) {
      let pet = master === Master.Boy ? new Dog() : new Cat();
      pet.eat(); // ok!
      pet.jump(); // error!
      return pet;
    }
    
  • 可区分的联合类型

    结合了联合类型和字面量类型的类型保护方法。
    如果一个类型是多个类型的联合类型,并且多个类型之间有公共属性,那么我们可以凭借这个公共属性创建类型保护区块。

    // 计算不同图形的面积
    
    interface Square {
      kind: "square";
      size: number;
    }
    
    interface Rectangle {
      kind: "rectangle";
      width: number;
      height: number;
    }
    
    function computedArea(shape: Square | Rectangle) {
      switch (shape.kind) {
        case "square":
          return shape.size * shape.size;
        case "rectangle":
          return shape.width * shape.height;
      }
    }
    

    这时,我希望增加一个 圆形,会出现隐患。

    interface Circle {
      kind: "circle";
      r: number;
    }
    function computedArea(shape: Square | Rectangle | Circle) {
      ...
    }
    computedArea({ kind: "circle", r: 1 }); // undefinded
    

    这时并没有给出错误提示。约束方案有二:

    • 指定明确返回值类型
      type Shape = Square | Rectangle | Circle;
      function computedArea(shape: Shape): number {
        switch (shape.kind) {
          case "square":
            return shape.size * shape.size;
          case "rectangle":
            return shape.width * shape.height;
          case "circle":
            return Math.PI * shape.r ** 2;
        }
      }
      
    • 利用 never 类型
      function computedArea(shape: Shape) {
        switch (shape.kind) {
          case "square":
            return shape.size * shape.size;
          case "rectangle":
            return shape.width * shape.height;
          case "circle":
            return Math.PI * shape.r ** 2;
          default:
            return ((e: never) => {
              throw new Error(e);
            })(shape);
        }
      }
      
      检查 shape 是否是 never 类型;当 case 完全覆盖时,shapenever 类型;反之,需要补全分支。

索引类型

一个常见的 Javascript 模式:从对象中选取属性的子集。

let Obj = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
};
function getValues(obj: any, keys: string[]) {
  return keys.map((key) => obj[key]);
}
getValues(Obj, ["a", "b"]);
getValues(Obj, ["e", "f"]);

我们如何来约束传参 keysobj 中存在的索引值呢?

interface Obj {
  a: number;
  b: number;
  c: number;
  d: number;
}
let obj: Obj = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
};
function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
  return keys.map((key) => obj[key]);
}
getValues(obj, ["a", "b"]);
  • keyof T:索引类型查询操作符

    对于任何类型 T, keyof T 的结果为 T 上已知的公共属性名的联合。

    interface Obj {
      a: number;
      b: number;
      c: number;
      d: number;
    }
    let objProps: keyof Obj; // "a" | "b" | "c" | "d"
    
  • T[K]:索引访问操作符

    在这里,类型语法反映了表达式语法。

映射类型

Typescript 提供了从旧类型中创建新类型的一种方法。

interface Flags {
  option1: boolean;
  option2: boolean;
}

type ReadonlyFlags = Readonly<Flags>;

/*
  type ReadonlyFlags = {
      readonly option1: boolean;
      readonly option2: boolean;
  }
*/

/**
 * lib.es5.d.ts
 * Make all properties in T readonly

  type Readonly<T> = {
      readonly [P in keyof T]: T[P];
  };

 */
type PartialFlags = Partial<Flags>;
/*
  type PartialFlags = {
      option1?: boolean | undefined;
      option2?: boolean | undefined;
  }
*/
type PickFlags = Pick<Flags, "option1">;
/*
  type PickFlags = {
      option1: boolean;
  }
*/
type PickFlags = Pick<Flags, "option1" | "option2">;

ReadonlyPartialPick 是同态的,但 Record 不是。因为 Record 并不需要输入类型来拷贝属性,所以它不属于同态:

type twoStringProps = Record<"prop1" | "prop2", string>;
/*
  type twoStringProps = {
      prop1: string;
      prop2: string;
  }
*/

条件类型

TypeScript 2.8 引入了有条件类型,它能够表示非统一的类型。

T extends U ? X : Y

T 能够赋值给 U,那么类型是 X,否则为 Y

type TypeName<T> = T extends string
  ? "string"
  : T extends number
  ? "number"
  : "object";
  • Exclude \ Extract \ NotNullable

    type T1 = Exclude<"a" | "b" | "c", "a" | "e">;
    // Exclude<"a", "a" | "e"> |  Exclude<"b", "a" | "e"> |  Exclude<"c", "a" | "e">
    // never | "b" | "c"
    // "b" | "c"
    
    type T2 = Extract<"a" | "b" | "c", "a" | "e">;
    // T2 = "a"
    
    type T3 = NotNullable<number | string | null>;
    // type T3 = string | number
    
  • 有条件类型中的类型推断: ReturnType
    现在在有条件类型的 extends 子语句中,允许出现 infer 声明,它会引入一个待推断的类型变量。

    type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
    
    type T4 = ReturnType<() => number>;
    

官方实现


/**
* Make all properties in T optional
*/
type Partial<T> = {
    [P in keyof T]?: T[P];
};

/**
* Make all properties in T required
*/
type Required<T> = {
    [P in keyof T]-?: T[P];
};

/**
* Make all properties in T readonly
*/
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
    [P in K]: T;
};

/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;

/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;

/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T extends null | undefined ? never : T;

/**
* Obtain the parameters of a function type in a tuple
*/
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

/**
* Obtain the parameters of a constructor function type in a tuple
*/
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;

/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

/**
* Obtain the return type of a constructor function type
*/
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;

/**
* Convert string literal type to uppercase
*/
type Uppercase<S extends string> = intrinsic;

/**
* Convert string literal type to lowercase
*/
type Lowercase<S extends string> = intrinsic;

/**
* Convert first character of string literal type to uppercase
*/
type Capitalize<S extends string> = intrinsic;

/**
* Convert first character of string literal type to lowercase
*/
type Uncapitalize<S extends string> = intrinsic;

TypeScript 入门系列