typescript(一)

268 阅读7分钟

基础类型

  1. Boolean

  2. Number

  3. String

  4. Array

  5. Tuple 元组

    元组类型允许标识一个已知元素数量和类型的数组,各元素的类型不必相同。

    // Declare a tuple type
    let x: [string, number];
    // Initialize it
    x = ['hello', 10]; // OK
    // Initialize it incorrectly
    x = [10, 'hello']; // Error
    
  6. enum 枚举

    使用枚举类型可以为一组数值赋予友好的名字。

    默认情况下,从0开始为元素编号。 你也可以手动的指定成员的数值。

    enum Color {Red, Green, Blue}
    let c: Color = Color.Green;
    console.log(c);//1
    
  7. any

  8. Void

    void类型与any类型相反,它表示没有任何类型

  9. Null和Undefined

    默认情况下nullundefined是所有类型的子类型。

    当你指定了--strictNullChecks标记,nullundefined只能赋值给void和它们各自。

  10. never

    never类型表示的是那些永不存在的值的类型。

    never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使any也不可以赋值给never

    // 返回never的函数必须存在无法达到的终点
    function error(message: string): never {
        throw new Error(message);
    }
    
    // 推断的返回值类型为never
    function fail() {
        return error("Something failed");
    }
    
    // 返回never的函数必须存在无法达到的终点
    function infiniteLoop(): never {
        while (true) {
        }
    }
    

11.Object

高级类型

  1. 交叉类型

    交叉类型是将多个类型合并为一个类型。

    包含了所需的所有类型的特性。

    eg:Person & Serializable & Loggable

  2. 联合类型

    联合类型表示一个值可以是几种类型之一。

    用竖线(|)分隔每个类型。

    只能访问此联合类型的所有类型里共有的成员。

  3. 字符串字面量类型

    字符串字面量类型允许你指定字符串必须的固定值。

    type Easing = "ease-in" | "ease-out" | "ease-in-out";
    
  4. 数字字面量类型

  5. 索引类型

    使用索引类型,编译器就能够检查使用了动态属性名的代码

    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[]
    

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

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

  6. 映射类型

    从旧类型中创建新类型的一种方式

    它的语法与索引签名的语法类型,内部使用了for .. in。 具有三个部分:

    • 类型变量K,它会依次绑定到每个属性。
    • 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
    • 属性的结果类型。
    // TypeScript的标准库lib.d.ts
    
    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;
    }
    
    // 映射Person
    type PersonPartial = Partial<Person>;
    type ReadonlyPerson = Readonly<Person>;
    
    type Keys = 'option1' | 'option2';
    type Flags = { [K in Keys]: boolean };
    
    //等同于
    type Flags = {
        option1: boolean;
        option2: boolean;
    }
    

索引类型和字符串索引签名

keyofT[K]与字符串索引签名进行交互。 如果你有一个带有字符串索引签名的类型,那么keyof T会是string。 并且 T[string]为索引签名的类型:

interface Map<T> {
    [key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number

Symbols类型

symbol类型的值是通过Symbol构造函数创建的

Symbols是不可改变且唯一的

Symbols也可以被用作对象属性的键

let sym = Symbol();

let obj = {
    [sym]: "value"
};

console.log(obj[sym]); // "value"

Symbols也可以与计算出的属性名声明相结合来声明对象的属性和类成员

const getClassNameSymbol = Symbol();

class C {
    [getClassNameSymbol](){
       return "C";
    }
}

let c = new C();
let className = c[getClassNameSymbol](); // "C"

Symbol的API的了解:

image.png

  • Symbol.hasInstance 用来识别一个对象是否是其实例
  • Symbol.isConcatSpreadable boolean,数组元素是否可展开
  • Symbol.match 正则表达式用来匹配字符串
  • Symbol.replace 正则表达式用来替换字符串中匹配的子串
  • Symbol.search 正则表达式返回被匹配部分的字符串中的索引
  • Symbol.species 函数值,为一个构造函数。用来创建派生对象
  • Symbol.split 分割字符串
  • Symbol.toPrimitive 把对象转换为响应的原始值
  • Symbol.toStringTag 返回创建对象时默认的字符串描述

类型守卫是为了解决什么问题

用联合类型对象共有的方法时,一切正常,但是使用联合类型对象各自独有的方法时,ts 会报错

类型守卫的方法

  1. 类型断言(as<type>identifier!
  2. 用户自定义的类型保护 要定义一个类型守卫,我们只要简单地定义一个函数,它的返回值是一个类型谓词
function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined;
}

pet is Fish就是类型谓词。 谓词为parameterName is Type这种形式,parameterName必须是来自于当前函数签名里的一个参数名。

// 'swim' 和 'fly' 调用都没有问题了

if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}
  1. typeof typeof类型守卫只有两种形式能被识别:typeof v === "typename"typeof v !== "typename""typename"必须是 "number""string""boolean""symbol"

但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型守卫。

  1. instanceof instanceof类型守卫是通过构造函数来细化类型的一种方式。 instanceof的右侧要求是一个构造函数,TypeScript将细化为:
  • 此构造函数的prototype属性的类型,如果它的类型不为any的话
  • 构造签名所返回的类型的联合

类型别名(type)的使用

  1. 类型别名可以作用于原始值,联合类型,元组以及其他任何你需要手写的类型
  2. 类型别名也可以是泛型
type Container<T> = { value: T };
// 可以使用类型别名来在属性里引用自己
type Tree<T> = {
    value: T;
    left: Tree<T>;
    right: Tree<T>;
}
  1. 类型别名可以与交叉类型一起使用
type LinkedList<T> = T & { next: LinkedList<T> };

interface Person {
    name: string;
}

var people: LinkedList<Person>;
var s = people.name;
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;

注意: 类型别名不能出现在声明右侧的任何地方

type Yikes = Array<Yikes>; // error

类型别名(type)和接口(interface)的区别

  1. 类型别名会给类型起个新名字,来引用那个类型;接口创建了一个新的名字,可以在其他任何地方使用。
  2. 类型别名不能被extendsimplements;而接口可以。
  3. 通常会使用类型别名来描述一个类型并且需要使用联合类型或元组类型;而接口不行。

单例类型

多数是指枚举成员类型和数字/字符串字面量类型

可辨识联合

合并单例类型,联合类型,类型保护和类型别名来创建一个叫做可辨识联合的高级模式,它也称做标签联合代数数据类型

具有3个要素:

  1. 具有普通的单例类型属性——可辨识的特征
  2. 一个类型别名包含了那些类型的联合——联合
  3. 此属性上的类型保护。
// 声明将要联合的接口
// 每个接口都有 kind属性但有不同的字符串字面量类型
// kind属性称作可辨识特征或标签
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;
    }
}

完整性检查

当没有涵盖所有可辨识联合的变化时,我们想让编译器可以通知我们,有两种方式可以实现。 比如,如果我们添加了 Triangle到 Shape,我们同时还需要更新 area。

  • 启用--strictNullChecks并且指定一个返回值类型
function area(s: Shape): number { // error: returns number | undefined
    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;
    }
}
  • 使用 never类型,编译器用它来进行完整性检查
// 检查 s 是否为 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
    }
}

备注:整理不易,您的鼓励是我的动力,感谢!感谢!感谢!