TypeScript从基础到进阶 | 青训营笔记

95 阅读4分钟

基础

基础类型

  1. boolean、number、string

    1. ts中最常见的数据类型
  2. 枚举 enum

    1. 表示一组常量值,形成了常量和值的一一映射
    enum Color {
      Red = "#FF0000",
      Green = "#00FF00",
      Blue = "#0000FF"
    }
    
    function getColorName(color: Color): string {
      switch (color) {
        case Color.Red:
          return "红色";
        case Color.Green:
          return "绿色";
        case Color.Blue:
          return "蓝色";
      }
    }
    
    console.log(getColorName(Color.Red)); // 输出:红色
    
  3. any、unknown、void

    1. unknown类型的变量无法直接使用,经过类型判断or类型断言可以使用,对类型安全更友好
    2. void类型的变量只能赋值为null或undefined
  4. never

    1. 表示不应该出现的值,抛出异常or死循环函数。
    function error(message: string): never {
      throw new Error(message);
    }
    
    // 调用 error 函数,导致程序抛出异常
    error("An error has occurred.");
    
  5. 数组

    1. number[]
    2. 数组泛型
    3. 接口(不推荐)
    4. 类数组(只能用接口),常用的类数组都有官方的定义IArgumentsNodeListHTMLCollection
  6. 元组 tuple

    1. 可用在函数的返回值为多种类型时
    let tom: [string, number] = ['Tom', 25];
    

函数

支持可选参数、默认参数、剩余参数。

函数重载:函数名称相同,参数类型和数量不同,支持多种类型。

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

接口 interface

  1. 规定对象or函数类型
interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    return source.search(subString) !== -1;
}
  1. 支持只读属性、可选属性、任意属性,普通属性不能多不能少,类型也要完全一致。
// 其他属性类型必须是任意属性的类型的子集
interface Person {
    name: string;
    age?: number;
    [propName: string]: string | number;
}

  1. 增加public、private、protected修饰符

    构造函数被private修饰,不允许继承和实例化 构造函数被protected修饰,不允许被实例化

  2. 抽象类

    1. 不能被实例化,只能被继承
    2. 基类,抽象方法必须被子类实现
    abstract class Animal {
      public name;
      public constructor(name) {
        this.name = name;
      }
      public abstract sayHi();
    }
    
    class Cat extends Animal {
      public eat() {
        console.log(`${this.name} is eating.`);
      }
    }
    
    let cat = new Cat('Tom');
    
    // index.ts(9,7): error TS2515: Non-abstract class 'Cat' does not implement inherited abstract member 'sayHi' from class 'Animal'.
    
  3. interface、类和implements

    1. 将不同类之间的共有特性抽象成接口,一个接口可以被多个类implements,一个类也可以implements多个接口
    interface Alarm {
        alert(): void;
    }
    
    interface Light {
        lightOn(): void;
        lightOff(): void;
    }
    
    class Car implements Alarm, Light {
        alert() {
            console.log('Car alert');
        }
        lightOn() {
            console.log('Car light on');
        }
        lightOff() {
            console.log('Car light off');
        }
    }
    
    💡 接口继承类 声明class A时,除了常见名字为A的类,还创建了名字为A的类型(实例的类型,不包含构造函数、静态属性、静态方法)。 因此接口可继承类,等同于继承接口。

进阶

高级类型

  1. 联合类型 |

  2. 交叉类型 &

  3. 类型断言

    // 非空断言
    let myString: string | undefined = "Hello World";
    let strLength: number = myString!.length; // 非空断言,告诉编译器 myString 不为空
    // 赋值断言
    let myNumber: any = "123"; // myNumber 的类型为 any
    let numLength: number = (myNumber as string).length; // 赋值断言,将 myNumber 类型转换为 string
    
  4. 类型别名

    1. 相同点

      都可以定义对象or函数

      都允许继承

    2. 不同点

      定义对象 vs 定义别名

      type可定义基本类型,interface不行

      interface可合并重复声明,type不行

涉及到联和和交叉用type,涉及到类用interface,其他情况好像都可以

// 字符串字面量类型
type EventNames = 'click' | 'scroll' | 'mousemove';

泛型

使用场景:

定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

官方定义(设计哲学):API需要考虑可重用性,组件不仅能支持当前的数据类型,还要能支持未来的数据类型。C#和Java都可以使用泛型。

使用方法:

function createArray<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

// 自动推算类型
createArray(3, 'x'); // ['x', 'x', 'x']
// 也可以手动指定
createArray<string>(3, 'x'); // ['x', 'x', 'x']

泛型的作用是临时占位

  1. 泛型约束

    函数内使用泛型变量时,不知道它是什么类型,使用属性or方法编译会报错。

    可以定义一个接口,里面包含我们需要使用的属性or方法。然后让泛型extends这个接口。

    同时,如果调用函数时,传入的参数没有这些属性or方法,编译会报错。

    interface Lengthwise {
        length: number;
    }
    
    function loggingIdentity<T extends Lengthwise>(arg: T): T {
        console.log(arg.length);
        return arg;
    }
    
    loggingIdentity(7);
    
    // index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.
    

    泛型 也可以 extends泛型。

    function copyFields<T extends U, U>(target: T, source: U): T {
        for (let id in source) {
            target[id] = (<T>source)[id];
        }
        return target;
    }
    
    let x = { a: 1, b: 2, c: 3, d: 4 };
    
    copyFields(x, { b: 10, d: 20 });
    
  2. 泛型类

    class GenericNumber<T> {
        zeroValue: T;
        add: (x: T, y: T) => T;
    }
    
    let myGenericNumber = new GenericNumber<number>();
    myGenericNumber.zeroValue = 0;
    myGenericNumber.add = function(x, y) { return x + y; };
    
  3. 泛型默认类型

    ts 2.3后

    当没有指定泛型类型,且无法推断时,使用默认类型。

    function createArray**<T = string>**(length: number, value: T): Array<T> {
        let result: T[] = [];
        for (let i = 0; i < length; i++) {
            result[i] = value;
        }
        return result;
    }
    

操作符

typeof、keyof

type Keys = "a" | "b"

// in:遍历枚举类型
type Obj = {
	[p in Keys]: any
} // -> {a:any, b:any}

// T[K]
let type1: Obj["a"] //any

// extends:泛型约束

常用工具类型

Partial

Required

Readonly

Pick

Record

等。。。

image.png

Partial:先遍历T的key,通过索引获取类型,通过可选操作符重新定义为可选的

实战

声明文件

image.png

实战场景一:约束后端接口类型

image.png

参考

官方文档

TypeScript入门教程

深入理解TypeScript

github.com/dzharii/awe…

ts playground: ts编译js