给一个类型起个新名字:类型别名

类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {...}

类型别名不能被 extendsimplements

类型字面量

字符串字面量类型

字符串字面量类型允许你指定字符串必须的固定值。 在实际应用中,字符串字面量类型可以与联合类型,类型保护和类型别名很好的配合。 通过结合使用这些特性,你可以实现类似枚举类型的字符串。

使用固定的字面量字符串代替string作为更精确的类型定义

只能从三种允许的字符中选择其一来做为参数传递,传入其它值则会产生错误

type Easing = "ease-in" | "ease-out" | "ease-in-out";

可通过类型保护,实现面向字面量的类型断言

if (easing === "ease-in")

枚举字面量也可作为类型使用,

如我们在 枚举一节里提到的,当每个枚举成员都是用字面量初始化的时候枚举成员是具有类型的。

数字字面量也可作为类型使用。

函数式编程利器:可辨识联合

可辨识联合在函数式编程很有用处。 它具有3个要素:

  1. 具有普通的单例类型属性— 可辨识的特征。 => 定义多个带有标志位的接口
  2. 一个类型别名包含了那些类型的联合— 联合。 => 联合类型将多个接口变作一个接口
  3. 此属性上的类型保护。 => 在类型保护下 根据标志位不同执行不同代码块
interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}
type Shape = Square | Rectangle | Circle;

在使用时添加类型保护,不同类型执行不同代码块。

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.radius ** 2;
    }
}

上面的例子中,area的case中包含了参数类型Shape可能存在的所有类型,但是如果Shape中的类型增加了,area函数可能因没有符合类型的case而返回undefined,这是不好的,解决此问题我们可使用never类型。

never类型—即为除去所有可能情况后剩下的类型

function assertNever(x: never): never {
    throw new Error("Unexpected object: " + x);
}
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.radius ** 2;
        default: return assertNever(s); // error here if there are missing cases
    }
}

此方法为没有对应的case时,将变量传入一个函数中,该函数参数类型定义为never,在此函数中对意料外的参数进行其他操作

链式开发原理:多态的this

一个类或对象中的方法返回this,以方便构成链式操作。

面试必问keyof:索引类型

通过 索引类型查询索引访问操作符来定义参数类型,面对一个函数中传入两个参数,参数A为参数B的部分索引,用来筛选处参数B的部分内容。

索引类型查询操作符:keyof T

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

索引访问操作符:T[K]

function pluck(o, names) {
    return names.map(n => o[n]);
}
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map(n => o[n]);
}

interface Person {
    name: string;
    age: number;
}
let person: Person = {
    name: 'Jarid',
    age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]
interface Map<T> {
    [key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number

## 类型直接的转换与继承:映射类型

在映射类型里,新类型以相同的形式去转换旧类型里每个属性。 例如,你可以令每个属性成为 readonly类型或可选的。

type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };

// ===

type Flags = {
    option1: boolean;
    option2: boolean;
}
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}
type Partial<T> = {
    [P in keyof T]?: T[P];
}
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
}
type Record<K extends string, T> = {
    [P in K]: T;
}

type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>

ReadonlyPartialPick是同态的,但 Record不是。 因为 Record并不需要输入类型来拷贝属性,所以它不属于同态,非同态类型本质上会创建新的属性,因此它们不会从它处拷贝属性修饰符。

  • Exclude<T, U> – 从T中剔除可以赋值给U的类型。
  • Extract<T, U> – 提取T中可以赋值给U的类型。
  • NonNullable<T> – 从T中剔除nullundefined
  • ReturnType<T> – 获取函数返回值类型。
  • InstanceType<T> – 获取构造函数类型的实例类型。