IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    TypeScript 复习总结(一) · 看不见我的美 · 是你瞎了眼

    馬腊咯稽发表于 2020-02-08 00:00:00
    love 0
    TypeScript 复习总结(一)

    原始类型(一):boolean、number、string、void、undefined、null、symbol、bigint

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    // 不是 Boolean,否则会被认为是布尔对象
    const isOk: boolean = true;
    // 十进制、十六进制、二进制都是 number 类型
    const num: number = 666;
    const str: string = 'hahasha';
    const sym: symbol = Symbol('sym');
    // JS 安全整数范围有限,可以使用 bigint 类型解决,ES6 也引入了 BigInt
    const bigInt: bigint = BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1);
    // strictNullChecks = true
    const a: void = undefined; // pass
    const a: void = null; // fault
    const a: null = undefined; // fault
    const a: undefined = null; // fault
    // strictNullChecks = false
    const a: void = undefined; // pass
    const a: void = null; // pass
    const a: null = undefined; // pass
    const a: undefined = null; // pass
    

    原始类型(二):any、unknown、never、object、数组、元组

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    
    // 检查器不会对 any 类型进行类型检查而直接进行编译
    let anyType: any;
    anyType = 'hahasha';
    anyType = 0;
    anyType.foo.bar; // pass
    anyType[0][1]; // pass
    new anyType(); // pass
    anyType(); // pass
    /**
     * unknown 类型的值可以被赋值给任何类型
     * 当 unknown 类型被确定是某个类型之前
     * 不可以被进行任何操作比如实例化等
     */
    let unknownType: unknown;
    unknownType = 'hahasha';
    unknownType = false;
    unknownType.foo.bar; // fault
    unknownType[0][1]; // fault
    new unknownType(); // fault
    unknownType(); // fault
    function example(v: unknown): string {
     if (v instanceof Date) {
     // v 的类型从 unknown 缩小到了 Date 实例,可以进行操作
     return v.toISOString();
     }
     return String(v);
    }
    // never 类型表示永远不存在的类型
    type people = 'male' & 'female'; // 不同字符串类型不能相交,故为 never
    const emptyArr: never[] = []; // 永远是空数组
    function error(e: string): never {
     // 抛出异常,永远不会有返回值
     throw new Error(e);
    }
    // 数组类型定义方式
    const list1: Array<number> = [1, 2, 3, 4];
    const list2: number[] = [5, 6, 7, 8];
    // 元组表示一个已知元素数量和类型的数组
    const tuple1: [string, number, boolean] = ['hello', 123, true]; // pass
    // 元组中的元素,必须与声明的类型一致,不能多、不能少、顺序不能错
    const tuple2: [string, number, boolean] = [true, 123, 'hello']; // fault
    const tuple3: [string, number, boolean] = ['hello', 'hello']; // fault
    // 元组继承于数组,拥有比数组更严格的类型检查
    interface Tuple extends Array<string | number | boolean> {
     0: string;
     1: number;
     2: boolean;
     length: 3;
    }
    // 元组越界问题
    const over: [string, number] = ['over', 0];
    over.push(1); // pass
    // 允许向元组中使用数组的 push 方法插入新元素
    console.log(over); // ["over", 0, 1];
    // 访问新加入的元素时,会报错
    console.log(over[2]); // fault
    // object 表示非原始类型,普通对象、枚举、数组、元组都是 object 类型
    let o: object;
    o = [1];
    o = {
     value: false
    };
    

    枚举类型:用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型,本质是 JS 对象

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    
    // 数字枚举
    enum Direction {
     Up,
     Down,
     Left,
     Right
    }
    console.log(Direction.Up === 0); // true
    console.log(Direction.Down === 1); // true
    console.log(Direction.Left === 2); // true
    console.log(Direction.Right === 3); // true
    // 把第一个值赋值后,后面也会根据第一个值进行累加
    enum Direction {
     Up = 10,
     Down,
     Left,
     Right
    }
    console.log(
     Direction.Up, // 10
     Direction.Down, // 11
     Direction.Left, // 12
     Direction.Right // 13
    );
    // 字符串枚举
    enum Direction {
     Up = 'Up',
     Down = 'Down',
     Left = 'Left',
     Right = 'Right'
    }
    console.log(
     Direction['Right'], // Right
     Direction.Up // Up
    );
    // 异构枚举
    enum BooleanLikeHeterogeneousEnum {
     No = 0,
     Yes = 'YES'
    }
    // 反向映射 name <=> value
    enum Direction {
     Up,
     Down,
     Left,
     Right
    }
    console.log(Direction[0]); // Up
    // 枚举的本质
    var Direction;
    (function (Direction) {
     Direction[(Direction['Up'] = 10)] = 'Up';
     Direction[(Direction['Down'] = 11)] = 'Down';
     Direction[(Direction['Left'] = 12)] = 'Left';
     Direction[(Direction['Right'] = 13)] = 'Right';
    })(Direction || (Direction = {}));
    // 联合枚举与枚举成员的类型
    enum Direction {
     Up,
     Down,
     Left,
     Right
    }
    const va = 0;
    console.log(va === Direction.Up); // true
    type vc = 0;
    declare let vb: vc;
    vb = 1; // fault
    vb = Direction.Up; // pass
    // 联合枚举类型
    enum Direction {
     Up,
     Down,
     Left,
     Right
    }
    declare let a: Direction;
    enum Animal {
     Dog,
     Cat
    }
    a = Direction.Up; // pass
    a = Animal.Dog; // fault
    // 枚举合并
    enum Direction {
     Up = 'Up',
     Down = 'Down',
     Left = 'Left',
     Right = 'Right'
    }
    enum Direction {
     Center = 1
    }
    // 转换为 JS 后
    var Direction;
    (function (Direction) {
     Direction['Up'] = 'Up';
     Direction['Down'] = 'Down';
     Direction['Left'] = 'Left';
     Direction['Right'] = 'Right';
    })(Direction || (Direction = {}));
    (function (Direction) {
     Direction[(Direction['Center'] = 1)] = 'Center';
    })(Direction || (Direction = {}));
    // 为枚举添加静态方法(借助命名空间)
    enum Month {
     January,
     February,
     March,
     April,
     May,
     June,
     July,
     August,
     September,
     October,
     November,
     December
    }
    namespace Month {
     export function isSummer(month: Month) {
     switch (month) {
     case Month.June:
     case Month.July:
     case Month.August:
     return true;
     default:
     return false;
     }
     }
    }
    console.log(Month.isSummer(Month.January)); // false
    

    接口:为类型命名、为你的代码或第三方代码定义契约

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
    // 使用接口来描述 Cook 函数
    interface Cook {
     (foods: string): string;
    }
    // 可索引类型
    interface Mail {
     [name: string]: string;
    }
    // 使用接口来描述 User 对象
    interface User {
     name: string;
     age?: number; // 可选属性
     readonly isMale: boolean; // 只读属性
     say: (words: string) => string; // 函数属性
     cook: Cook; // 函数属性
     mail: Mail;
    }
    const getUserName = (user: User) => user.name;
    // 属性检查
    interface Config {
     width?: number;
    }
    function CalculateAreas(config: Config): { area: number } {
     let square = 100;
     if (config.width) {
     square = config.width * config.width;
     }
     return { area: square };
    }
    // 如果对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误
    let mySquare = CalculateAreas({ widdth: 5 }); // 这里传入的是 widdth 不是 width,TS 会认为这段代码有问题
    let mySquare = CalculateAreas({ widdth: 5 } as Config); // 可以使用类型断言解决错误
    interface Config {
     width?: number;
     [propName: string]: any; // 可以给 Config 类型添加字符串索引
    }
    // 继承接口
    interface VIPUser extends User {
     broadcast: () => void;
    }
    

    类

    接口与类的区别:

    • interface 是仅存在于 TS 上下文中的一种虚拟结构,TS 依赖接口用于类型检查,编译为 JS 后,接口将会被移除;
    • class 作为 TS 的一种变量类型存在于上下文之中,class 可以提供变量、方法等的具体实现方式等,它的作用不仅仅是约束数据结构;
    • class 和 interface 都可以用来约束数据的结构,但是频繁使用 class 会使程序的性能受到影响。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    /**
     * 抽象类:其它派生类的基类,不同于接口,它可以包含成员的实现细节
     * abstract 关键字是用于定义抽象类和在抽象类内部定义抽象方法
     */
    abstract class Animal {
     abstract makeSound(): void;
     move(): void {
     console.log('move');
     }
    }
    // 抽象类不能直接实例化,需要创建子类集成基类
    class Cat extends Animal {
     makeSound() {
     console.log('miao');
     }
    }
    const tom = new Cat();
    cat.makeSound(); // miao
    cat.move(); // move
    /**
     * 访问限定符:public(默认)、private、protected
     * 当成员被设置为 private 之后,被此限定符修饰的成员是只可以被类的内部访问
     * 当成员被设置为 protected 之后,被此限定符修饰的成员是只可以被类的内部以及类的子类访问
     */
    class Car {
     protected run() {
     console.log('启动...');
     }
    }
    class GTR extends Car {
     init() {
     this.run();
     }
    }
    const car = new Car();
    const gtr = new GTR();
    car.run(); // fault
    gtr.init(); // 启动...
    gtr.run(); // fault
    

    函数

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    // TS 可以感知到函数的返回值是 number 类型
    const add = (a: number, b: number) => a + b;
    const decline: (a: number, b?: number) => number = (a, b) => a - (b ? b : 0);
    // 重载
    interface Direction {
     top: number;
     right?: number;
     bottom?: number;
     left?: number;
    }
    function assigned(all: number): Direction;
    function assigned(topAndBottom: number, leftAndRight: number): Direction;
    function assigned(
     top: number,
     right: number,
     bottom: number,
     left: number
    ): Direction;
    // 代码实现函数不可被调用
    function assigned(a: number, b?: number, c?: number, d?: any) {
     if (b === undefined && c === undefined && d === undefined) {
     b = c = d = a;
     } else if (c === undefined && d === undefined) {
     c = a;
     d = b;
     }
     return {
     top: a,
     right: b,
     bottom: c,
     left: d
     };
    }
    

    泛型:一种特殊的变量,只用于表示类型而不是值

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    
    function returnValue<T>(para: T): T {
     return para;
    }
    function swap<T, U>(tuple: [T, U]): [U, T] {
     let temp = tuple.concat();
     return temp.reverse();
    }
    // 泛型变量
    function getLength<T>(arg: Array<T>) {
     console.log(arg.length);
     return arg;
    }
    // 泛型接口
    interface ReturnItem<T> {
     (para: T): T;
    }
    const returnItem: ReturnItem<number> = para => para;
    // 泛型类
    class Stack<T> {
     private arr: T[] = [];
     public push(item: T) {
     this.arr.push(item);
     }
     public pop() {
     this.arr.pop();
     }
    }
    // 泛型约束:泛型似乎可以是任何类型,如果我们知道传入的泛型属于哪一类,比如属于 number 或者 string 其中之一,那么应该如何约束泛型
    type Params = number | string;
    // 泛型类型被约束为 number 或 string
    class Stack<T extends Params> {
     private arr: T[] = [];
     public push(item: T) {
     this.arr.push(item);
     }
     public pop() {
     this.arr.pop();
     }
    }
    function getValue<T extends object, U extends keyof T>(o: T, k: U) {
     return o[k];
    }
    // 泛型参数的默认类型
    interface DefaultType<T = boolean> {
     isMarried: T;
    }
    type isMarried = DefaultType<number>;
    const gwenIsMarried: isMarried = { isMarried: 1 };
    // 多重泛型约束
    interface First {
     doSomething(): number;
    }
    interface Second {
     doSomethingElse(): string;
    }
    interface Child extends First, Second {}
    class Demo<T extends Child> {
     private genericProperty: T;
     useT() {
     this.genericProperty.doSomething();
     this.genericProperty.doSomethingElse();
     }
    }
    // 也可以用 & 实现多重泛型约束
    class Demo<T extends First & Second> {
     private genericProperty: T;
     useT() {
     this.genericProperty.doSomething();
     this.genericProperty.doSomethingElse();
     }
    }
    // 泛型与 new
    function factory<T>(type: { new (): T }): T {
     return new type();
    }
    // 泛型条件类型
    interface Dictionary<T = unknown> {
     [prop: string]: T;
    }
    // 当 T 是类型 Dictionary 的子集时,就返回类型 V
    type VDictionary<T> = T extends Dictionary<infer V> ? V : never;
    type ChineseDictionary = Dictionary<'chinese'>;
    type Chinese = VDictionary<ChineseDictionary>; // 'chinese'
    // 泛型工具
    type CustomPartial<T> = {
     [P in keyof T]?: T[P];
    };
    type CustomRequired<T> = {
     [P in keyof T]-?: T[P];
    };
    type CustomRecord<K extends keyof any, T> = {
     [P in K]: T;
    };
    type CustomPick<T, K extends keyof T> = {
     [P in K]: T[P];
    };
    type CustomExclude<T, U> = T extends U ? never : U;
    type CustomExtract<T, U> = T extends U ? T : never;
    type CustomOmit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
    type CustomReturnType<T> = T extends (...args: any[]) => infer V ? V : never;
    

    类型断言与类型守卫

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    
    interface Person {
     name: string;
     age: number;
    }
    const person = {} as Person;
    person.name = 'hahasha';
    person.age = 18;
    // 双重断言
    const anyPerson = 'hahasha' as any as Person;
    // 类型守卫,缩小类型范围
    class Person {
     name = 'xiaomuzhu';
     age = 20;
    }
    class Animal {
     name = 'petty';
     color = 'pink';
    }
    function getSometing(arg: Person | Animal) {
     if (arg instanceof Person) {
     console.log(arg.color); // fault
     console.log(arg.age); // pass
     }
     if (arg instanceof Animal) {
     console.log(arg.age); // fault
     console.log(arg.color); // pass
     }
    }
    // 字面量类型守卫
    type Foo = {
     kind: 'foo'; // 字面量类型
     foo: number;
    };
    type Bar = {
     kind: 'bar'; // 字面量类型
     bar: number;
    };
    function doStuff(arg: Foo | Bar) {
     if (arg.kind === 'foo') {
     console.log(arg.foo); // pass
     console.log(arg.bar); // fault
     } else {
     console.log(arg.foo); // fault
     console.log(arg.bar); // pass
     }
    }
    

    高级类型:交叉类型、联合类型、类型别名

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    
    // 交叉类型是将多个类型合并为一个类型
    interface AnyObject {
     [prop: string]: any;
    }
    function mixin<T extends AnyObject, U extends AnyObject>(
     first: T,
     second: U
    ): T & U {
     const result = <T & U>{};
     for (let id in first) {
     (<T>result)[id] = first[id];
     }
     for (let id in second) {
     if (!result.hasOwnProperty(id)) {
     (<U>result)[id] = second[id];
     }
     }
     return result;
    }
    // 联合类型
    function formatCommandline(command: string[] | string) {
     let line = '';
     if (typeof command === 'string') {
     line = command.trim();
     } else {
     line = command.join(' ').trim();
     }
    }
    // 类型别名
    type some = boolean | string;
    const b: some = true; // pass
    const c: some = 'hello'; // pass
    const d: some = 123; // fault
    type Container<T> = { value: T };
    type Tree<T> = {
     value: T;
     left: Tree<T>;
     right: Tree<T>;
    };
    /**
     * interface 只能用于定义对象类型,而 type 还可以定义交叉、联合、原始类型等
     * interface 可以实现接口的 extends 和 implements
     * interface 可以实现接口合并声明
     */
    type Alias = { num: number };
    interface Interface {
     num: number;
    }
    declare function aliased(arg: Alias): Alias;
    declare function interfaced(arg: Interface): Interface;
    

    可辨识联合类型

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    
    /**
     * 字面量:真值字面量类型、数字字面量类型、枚举字面量类型、大整数字面量类型、字符串字面量类型
     * 字面量类型要和实际的值的字面量一一对应
     */
    const a: 2333 = 2333; // pass
    const b: 0x1919n = 6425n; // pass
    const c: 'xiaomuzhu' = 'xiaomuzhu'; // pass
    const g: 'github' = 'gitlab'; // fault
    const d: false = false; // pass
    // 字面量类型与联合类型结合模拟一个类似于枚举的效果
    type Direction = 'North' | 'East' | 'South' | 'West';
    function move(distance: number, direction: Direction) {}
    // 类型字面量,跟 JS 中的对象字面量的语法很相似
    type Foo = {
     readonly [Symbol.iterator]: 'github';
     baz: [number, 'xiaomuzhu'];
     toString(): string;
     0x1: 'foo';
     bar: 12n;
    };
    // 可辨识联合类型
    type UserAction =
     | {
     id: number;
     action: 'delete';
     info: Info;
     }
     | {
     action: 'create';
     info: Info;
     };
    const UserReducer = (userAction: UserAction) => {
     // 类型守卫
     switch (userAction.action) {
     case 'delete':
     console.log(userAction.id);
     break;
     default:
     break;
     }
    };
    

    装饰器:在多个不同的类之间共享或者扩展一些方法或行为,而不是去直接修改它本身

    JS 需要配合 babel-plugin-transform-decorators-legacy,TS 需要配合 experimentalDecorators 选项。

    装饰器本质上是一个函数,@expression 其实是一个语法糖,@expression 求值后必须也是一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    
    // 类装饰器
    function addAge(constructor: Function) {
     constructor.prototype.age = 18;
    }
    @addAge
    class Person {
     name: string;
     age: number;
     constructor() {
     this.name = 'xiaomuzhu';
     }
    }
    console.log(new Person().age); // 18
    // 这段代码等同于
    Person = addAge(function Person() {});
    // 属性/方法装饰器
    function method(target: any, propKey: string, descriptor: PropertyDescriptor) {
     console.log(`target: ${target}`);
     console.log(`propKey: ${propKey}`);
     console.log(`descriptor: ${descriptor}`);
     descriptor.writable = false;
    }
    class Person {
     name: string;
     constructor() {
     this.name = 'xiaomuzhu';
     }
     @method
     say() {
     return 'instance method';
     }
     static run() {
     return 'static method';
     }
    }
    const xmz = new Person();
    // 修改实例方法
    xmz.say = function () {
     return 'hello';
    };
    /**
     * target: Person { say: [Function] }
     * propKey: say
     * descriptor: {"writable":true,"enumerable":true,"configurable":true}
     *
     * target: [Function: Person] { run: [Function] }
     * propKey: run
     * descriptor: {"writable":true,"enumerable":true,"configurable":true}
     *
     * xmz.say = function() {
     * ^
     * TypeError: Cannot assign to read only property 'say' of object '#<Person>'
     */
    // 参数装饰器
    function logPara(target: object, propKey: string, index: number) {
     // Person { greet: [Function] } greet 1
     // Person { greet: [Function] } greet 0
     console.log(target, propKey, index);
    }
    class Person {
     greet(@logPara message: string, @logPara name: string): string {
     return `${message} ${name}`;
     }
    }
    const p = new Person();
    p.greet('hello', 'xiaomuzhu');
    

    赋值断言、is 关键字、可调用类型注解和类型推导

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    
    // 明确赋值断言:将 ! 放置在实例属性和变量声明之后,来表明此属性已经确定它已经被赋值了
    let x: number;
    initialize();
    console.log(x! + x!); // pass
    function initialize() {
     x = 10;
    }
    // is 关键字
    function isString(test: any): test is string {
     // 判断 test 是不是 string 类型,并根据结果返回 boolean 类型
     return typeof test === 'string';
    }
    function example(foo: number | string) {
     if (isString(foo)) {
     /**
     * 如果将 test is string 修改为 boolean 将报错
     * 类型 string|number 上不存在 length 属性
     */
     console.log(foo.length);
     }
    }
    example('hello world');
    // 可调用类型注解
    interface A {
     (): string;
    }
    declare const a: A;
    a(); // pass
    interface B {
     new (): string;
    }
    declare const b: B;
    new b(); //pass
    // 类型推导
    const bar = [1, 2];
    let [a, b] = bar;
    a = 'hello'; // Error:不能把 'string' 类型赋值给 'number' 类型
    

    索引类型、映射类型

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    // keyof 索引类型查询操作符,用来获取所有 public 属性名构成的联合类型
    class Image {
     src: string = '';
     alt: string = '';
     width: number = '';
    }
    type propsNames = keyof Images; // "src" | "alt" | "width"
    // T[K] 索引访问操作符
    type propsType = Images[propsName]; // "string" | "number"
    function pick<T, K extends keyof T>(o: T, names: K[]): T[K][] {
     return names.map(n => o[n]);
    }
    // [K in Keys] 映射类型,K:类型变量,对应每个属性名的类型;Keys:字符串字面量构成的联合类型,表示一组属性名(的类型)
    interface User {
     username: string;
     id: number;
     token: string;
     avatar: string;
     role: string;
    }
    type partial<T> = { [K in keyof T]?: T[K] };
    type partialUser = partial<User>; // 所有属性都变成了可选类型
    

    条件类型

    1
    2
    3
    4
    5
    
    type Z = T extends U ? X : Y; // 若 T 能够赋值给 U,那么类型是 X,否则为 Y
    declare function f<T extends boolean>(x: T): T extends true ? string : number;
    f(Math.random() < 0.5); // string | number
    f(false); // number
    f(true); // string
    

    参考

    • 深入理解 TypeScript


沪ICP备19023445号-2号
友情链接