TypeScript官网内容解读

lxf2023-03-18 07:15:01

在AdminJS社区发布文章,并在正文的第一句加入“我正在参加「AdminJS·启航计划」”

typescript官网

typesrcipt线上测试

该总结都是查看官网,然后自己不理解的地方记录下来的。

typescript官网的几张图画的不错。

TypeScript官网内容解读

TypeScript官网内容解读

TypeScript官网内容解读

TypeScript官网内容解读

无交集的两个变量不能进行比较

if ("" == 0) { // 在js中是合法的,但是在ts中编译阶段会报错
}

TypeScript官网内容解读

定义一个函数声明

一般我们定义函数声明时,我们需要紧接着定义这个函数的实现,不然会报错。但是我们也可以使用declare来定义该函数的声明,让其在编译阶段不报错。

    // 定义声明, 这个不能编写具体实现。
    declare const backpack: Backpack<string | number> 

结构匹配

结构匹配,只匹配结构对象的字段的子集。 就是当我们传递比当前形参类型更大的类型对象时,编辑器并不会报错。这个发生在赋值的时候。

    interface IPoint {
      name: string,
      age: number
    }


    function foo(point: IPoint) {
      return `${point.name}`
    }

    const point = {
      z: 1, // 多定义一个z属性,传递给foo函数也是没关系的。
      age: 32,
      name: "eqeq"
    }

    foo(point)
// 结构匹配是对于赋值的时候发生的。
let o = { x: "hi", extra: 1 }; // ok
let o2: { x: string } = o; // ok

let, const 声明变量的区别

let 赋值时表示的是当前值得类型。const赋值时表示的是当前字面量类型。

let s = "right"; // string
const s1 = "right" // right

Readonly<T>

该泛型接口可以将传入的泛型类型全部变成只读类型。

    interface X {
      x: number;
      z: string;
      obj: {
        name: number,
      }
    }
    let rx: Readonly<X> = { x: 1, z: "z", obj: {
      name: 1
    } };
    rx.obj.name = 1; // ok
    rx.z = "21313" // error

让数组中的元素变成只读的方式

    // 数组只读
    let a: ReadonlyArray<number> = [1, 2, 3];
    let b: readonly number[] = [1, 2, 3];
    let c = [1, 2, 3] as const;
    
    a[4] = 1 // error

    b[0] = 1 // error

    c[2] = 1 //error

类型断言

当我们需要将一个类型断言成另一个与之没有关系的类型时,我们需要将类型先转换成any, unknown然后在进行转换。

// 转换成没有交集的类型,我们需要将中间类型转换成更不具体的类型
const a2 = "expr" as any as number;

链判断运算符?.和非空断言!的区别

  • ?.是js本身就存在的语法!是ts独有的语法。
  • 二者都是用于表示取值之前的值是否存在,不存在将返回undefined,将不再向下取值。
  • 但是有时候在ts中?.不起作用。所以我们需要使用!。

函数重载

函数重载只看函数的声明,函数实现只是整合了全部的函数声明。

function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3); // 报错

我们可以用字符串或数组调用它。但是,我们不能用字符串或数组的值来调用它,因为TypeScript只能将函数调用解析为单个重载

function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
    return x.length;
}

len(""); // OK

len([0]); // OK

len(Math.random() > 0.5 ? "hello" : [0]); // 报错

unknown

unknown 未知类型表示任何值。这类似于any类型,但更安全,因为对未知值执行任何操作都是不合法的。

    function f1(a: any) {
      a.b(); // OK
    }
    function f2(a: unknown) {
      a.b(); // 错误
    }

never

用于处理异常函数的返回值。

function fail(msg: string): never {
  throw new Error(msg);
}

Function

可以表示函数的通用类型, 但是一般都不使用它,都是自己定义函数签名。

function doSomething(f: Function) {
    return f(1, 2, 3);
}

定义一个函数类型别名或者函数类型接口

这是因为函数也是一种对象,都具有

    // 定义一个函数类型别名,并可以设置属性
    type DescribableFunction = {
      description: string;
      (someArg: number): boolean;
    };
    function doSomething(fn: DescribableFunction) {
      console.log(fn.description + " returned " + fn(6));
    }
    interface FooFunction {
       name: string;
       (name: string): string
    }
    
    const foo: FooFunction = function(a) {
        return a
    }

泛型传入的类型,在函数中未设置返回值类型的时候,我们优先使用继承的类型,而非运行时传入的类型

因为TypeScript必须使用约束类型解析arr[0]表达式,而不是在调用期间“等待”解析元素。

    
    function firstElement1<Type>(arr: Type[]) {
      return arr[0];
    }

    function firstElement2<Type extends any[]>(arr: Type) {
      return arr[0]; // 返回值推断为any
    }

    // a: number (good)
    const a4 = firstElement1([1, 2, 3]);
    // b: any (bad)
    const b = firstElement2([1, 2, 3]);

固定参数的函数传递扩展实参时

如果我们定义的函数参数指定了长度,我们使用...解构传入的数组会报错的。因为数组是可以变得。

展开实参必须具有元组类型或传递给rest形参。

const args = [8, 5];
const angle = Math.atan2(...args); // 报错

解决办法

    // Inferred as 2-length tuple
    const args = [8, 5] as const;
    // OK
    const angle = Math.atan2(...args);

注意这里是让整个变量作为不可变的,所以不能使用下列方法去约束变量。

    ReadonlyArray<number> 
    readonly number[]

TypeScript官网内容解读

这种报错源自于ts严格的参数长度检查,而不是将多余的参数自动舍弃掉。

void

对于类型别名而言,虽然定义了函数不返回任何值,但是他还是可以返回任何值的,编译器并不报错。保留js的运行时。

    type voidFunc = () => void;

    const f1: voidFunc = () => {
      return true; // okk
    };

    const f2: voidFunc = () => true; // okk

    const f3: voidFunc = function () {
      return true; // okk
    };

对象类型属性的操作

对象类型中的每个属性都可以指定几件事:

  • 类型。
  • 属性是否是可选的。(通过使用?表示)
  • 属性是否可以被写入。(通过使用[]来制定是否可扩展属性)
    interface StringArray {
        [index: number]: string;
    }

并且所有的类型如果匹配上了属性的类型,那么属性值也必须是定义好的。如果属性的类型没匹配到,那么将没有影响。

    interface NumberDictionary {
      [index: string]: string;

      length: number; // 错误, length为字符串,匹配到了[index: string],但是他的类型值为number。
      name: string; // ok
    }
    
    
    interface NumberDictionary {
      [index: number]: string;

      length: number; // ok
      name: string; // ok 二者都没有匹配,所以不需要管。
    }
  • readonly写在属性前面表示该属性只读的。但是只是对当前属性一层。这个只是在编译时进行检查,如果关闭ts的检测,他的运行时行为和js一样。
interface Person {
    name: string;
    age: number;
}

interface ReadonlyPerson {
    readonly name: string;
    readonly age: number;
}

let writablePerson: Person = {
    name: "Person McPersonface",
    age: 42,
};
// works
let readonlyPerson: ReadonlyPerson = writablePerson;
console.log(readonlyPerson.age); // prints '42'
writablePerson.age++;
console.log(readonlyPerson.age); // prints '43'

对于对象解构并重新命名

对象解构并重新命名后,他的类型约束将变成重新命名的变量约束。 除非显示的指定解构参数类型。

interface Shape {
   shape: Shape;
  xPos?: number;
  yPos?: number;
}
declare function render(x: unknown);

function draw({ shape: Shape, xPos: number = 100 }: {shape: number, xPos: number}) {
  render(Shape);
  render(number);
}

draw({shape: 1, xPos: 1})

TypeScript官网内容解读

TypeScript官网内容解读

interface类型可以赋值给type类型

    interface Colorful {
        color: string;
    }

    interface Circle {
        radius: number;
    }

    type ColorfulCircle = Colorful & Circle;

泛型

接口中的使用

    interface Box<Type> {
        contents: Type;
    }

类型别名中的使用

    type Box<Type> = {
        contents: Type;
    };

类中使用

不能在静态属性或方法中使用泛型类型。

class GenericNumber<NumType> {
    zeroValue: NumType;
    add: (x: NumType, y: NumType) => NumType;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
    return x + y;
};

ReadonlyArray 的含义

其实就是readonly type[]的另一种语法。

他只表示当前数组的元素是不可变的(改变或者增加减少),但是当前数组还是可以随意赋值的。

    let x1: readonly number[] = [1,2,3];
    let y1: number[] = [];

    x1 = y1;
    x1[0] = 21;
    y1 = x1; // 报错

元组

主要是注意一下元组的可选元素。

元组可以通过写出问号(?在元素类型之后)。可选的元组元素只能出现在末尾,也会影响length的类型。

type Either2dOr3d = [number, number, number?];

function setCoordinate(coord: Either2dOr3d) {
    const [x, y, z] = coord;
    console.log(`Provided coordinates had ${coord.length} dimensions`);
}

并且可以使用...来声明任意参数。并且可以放在任意位置。所以设置任意参数的元组没有length属性。

    type StringNumberBooleans = [string, number, ...boolean[]];

    type StringBooleansNumber = [string, ...boolean[], number];

    type BooleansStringNumber = [...boolean[], string, number];

只读类型的变量不能赋值给可变变量。防止可变变量发生变化。

    let point = [3, 4] as const;
    function distanceFromOrigin([x, y]: [number, number]) {
        return Math.sqrt(x ** 2 + y ** 2);
    }

    distanceFromOrigin(point);

函数类型可以使用哪些类型表示

定义成这样也可以是因为,函数本来就可以设置属性。所以可以使用接口,类型别名对象来定义。

  • 类型别名对象
    // 定义一个函数类型别名,并可以设置属性
    type DescribableFunction = {
      description: string;
      (someArg: number): boolean;
    };
    function doSomething(fn: DescribableFunction) {
      console.log(fn.description + " returned " + fn(6));
    }
  • 接口
interface GenericIdentityFn {
  <Type>(arg: Type): Type;
  name: string;
}

function identity<Type>(arg: Type): Type {
  return arg;
}

let myIdentity: GenericIdentityFn = identity;
  • 直接声明
declare function pad(s: string, n: number, direction: "left" | "right"): string;

这种类型别名和接口表示的类型,我们定义的都是无名函数类型。如果定义一个有名函数,那么该类型将被作为具体的对象类型。

    // 作为函数类型
    // export type StringValidator = {
    //   (s: string): boolean;
    //   name: string;
    // }

    export interface StringValidator {
        (s: string): boolean;
        name: string;
    }
    
    // 作为具体定义的对象类型
    export interface StringValidator1 {
        isAcceptable(s: string): boolean;
        name: string;
    }

    const foo:  StringValidator = function(s: string) {
        return true
    }

    console.log(foo("2"))

如何表示类型

    c: { new (): Type }
    c: new () => Type
class BeeKeeper {
    hasMask: boolean = true;
}

class ZooKeeper {
    nametag: string = "Mikle";
}

class Animal {
    numLegs: number = 4;
}

class Bee extends Animal {
    keeper: BeeKeeper = new BeeKeeper();
}

class Lion extends Animal {
    keeper: ZooKeeper = new ZooKeeper();
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;
createInstance(Bee).keeper.hasMask;

keyof 类型操作符

keyof操作符接受对象类型,并生成其键的字符串或数字文本并集。

    type Point = { x: number; y: number };
    type P = keyof Point; // “x” | "y"

    const a: P = "x"

TypeScript官网内容解读

ReturnType类型

TypeScript官网内容解读


    function f() {
      return { x: 10, y: 3 };
    }
    type P = ReturnType<typeof f>; // ReturnType泛型传入一个函数类型,可以获取函数返回值当做类型返回。


    const a: P = {
      x: 2,
      y: 90
    }

索引访问类型

可以直接通索引去获取别的类型别名中的类型。

    type Person = { age: number; name: string; alive: boolean };

    type Age = Person["age"]; // number
    type I1 = Person["age" | "name"]; // string | number
    type I2 = Person[keyof Person]; //string | number | boolean

当使用索引时,我们只能使用类型别名,并不能使用变量名。

type Alive = "alive"

type I3 = Person[Alive]; // boolean

const key = "age";

type Age = Person[key]; // 报错

条件类型别名

一般和泛型一起使用效果更佳。

interface Animal {
    live(): void;
}

interface Dog extends Animal {
    woof(): void;
}

type Example1 = Dog extends Animal ? number : string; // number

type Example2 = RegExp extends Animal ? number : string; // string

映射类型别名

配合-, +来做到对readonly, ?的操作

移除readonly操作符。并不是删除具有readonly操作符的类型元素。

type CreateMutable<Type> = {

-readonly [Property in keyof Type]: Type[Property];

};

type LockedAccount = {
    readonly id: string;
    readonly name: string;
};

type UnlockedAccount = CreateMutable<LockedAccount>; 
// { id: string; name: string; }

移除?操作符。并不是删除具有?操作符的类型元素。

    //  移除?操作符

    type MaybeUser = {
      readonly id: string;
      name?: string;
      age?: number;
    };

    type MapMaybeUser<T> = {
      [prop in keyof T]-?: T[prop]
    }

    // 转化后的类型 { id: string; name: string; age: number; }
    const user: MapMaybeUser<MaybeUser> = {
      id: "dwadwa",
      age: 20,
      name: "dawdwa"
    }

    user.id = "213131" // 报错

使用as将类型重新映射

在TypeScript 4.1及以后版本中,你可以在映射类型中使用as子句重新映射映射类型中的键。 简单理解就是可以改变类型属性名称。

type Getters<Type> = {
    [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]
};

interface Person {
    name: string;
    age: number;
    location: string;
}
type LazyPerson = Getters<Person>; //{ getName: () => string; getAge: () => number; getLocation: () => string; }
    type A<T extends {name: string ,age: number}> = {
      [a in T as a["name"]]: string // 如果in直接操作泛型变量,那么我们必须设置as映射。这里不写as也是没有任何意义的。
    }

    type B = A<{name: "zh", age: 20}> 
    //{
    //  zh: string;
    // }

这里不写as也是没有任何意义的。 TypeScript官网内容解读

Exclude<T, U>

判断 T 是否继承自 U。

TypeScript官网内容解读

type RemoveKindField<Type> = {
    [Property in keyof Type as Exclude<Property, "kind">]: Type[Property] // never属性直接过滤掉,但是如果自己设置的never别名属性是不会过滤掉的。
};

interface Circle {
    kind: "circle";
    radius: number;
}

type KindlessCircle = RemoveKindField<Circle>; // { radius: number; }

拼接字面量类型别名

拼接时,内部只能使用类型别名。

    type World = "world";

    type Greeting = `hello ${World}`;

string & keyof Type相当于与。就是需要都满足。 TypeScript官网内容解读

为了帮助进行字符串操作,TypeScript包含了一组可用于字符串操作的类型。这些类型是编译器内置的性能,不能在.d。ts文件包含在TypeScript中。

  • Uppercase<StringType>, 将字符串中的每个字符转换为大写。
    type Greeting = "Hello, world"
    type ShoutyGreeting = Uppercase<Greeting> // "HELLO, WORLD"
  • Lowercase<StringType>, 将字符串中的每个字符转换为小写。
    type Greeting = "Hello, world"

    type QuietGreeting = Lowercase<Greeting> // "id-my_app"
  • Capitalize<StringType>, 将字符串中的第一个字符转换为等效的大写字母。

    type LowercaseGreeting = "hello, world";

    type Greeting = Capitalize<LowercaseGreeting>; // "Hello, world"
  • Uncapitalize<StringType>, 将字符串中的第一个字符转换为等效的小写字母。
    type UppercaseGreeting = "HELLO WORLD";

    type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>; // "hELLO WORLD"

class 类

strictPropertyInitialization

用于决定类类型是否需要初始化。

初始化表示可以直接初始化或者通过在构造函数中初始化(隐式的赋值也可以)。

    class GoodGreeter {
      name: string;

      constructor(name: string) {
        this.name = name;
      }
    }

注意,该字段需要在构造函数本身中初始化。TypeScript不会分析你从构造函数调用的方法来检测初始化,因为派生类可能会覆盖这些方法并初始化成员失败。

如果你想通过构造函数以外的方法明确地初始化一个字段(例如,可能一个外部库正在为你填充你的类的一部分),你可以使用明确的赋值断言操作符!

class OKGreeter {
  // Not initialized, but no error
  name!: string;
}
    class OKGreeter {
      // Not initialized, but no error
      name: string; // 在方法中初始化,报错

      sayName(name: string) {
        this.name = name
        return this.name
      }
    }

    const o = new OKGreeter();
    console.log(o.sayName("dawdw"))

构造函数重载

类构造函数签名和函数签名之间只有一些区别:

  • 构造函数不能有类型参数——这些参数属于外部类声明。
  • 构造函数不能有返回类型注释——返回的总是类实例类型。

getter, setter一些规则

TypeScript对访问器有一些特殊的推理规则:

  • 如果get存在但没有set,则属性自动为只读。
  • 如果未指定setter参数的类型,则从getter的返回类型推断出该参数。
  • getter和setter必须具有相同的成员可见性。 可以使用不同类型的访问器进行获取和设置。

类实现接口

他只是约束了该方法,但是并没有约束类型注释。implements子句不会改变类主体的检查方式或类型推断方式。

    interface Checkable {
      check(name: string): boolean;
    }

    class NameChecker implements Checkable {
      check(s) { // s any
        // Notice no error here
        return s.toLowercse() === "ok";
        //         ^?
      }
    }

接口中的可选属性,并不是被创建在类上。 而且参数的个数与类型一样,返回值也一样的方法。

赋予类型更具体的类型

当目标>= ES2022或useDefineForClassFields为true时,类字段将在父类构造函数完成后初始化,覆盖父类设置的任何值。当您只想为继承的字段重新声明更准确的类型时,这可能会成为一个问题。为了处理这些情况,你可以写· declare来告诉TypeScript这个字段声明不应该有运行时效应。

interface Animal {
    dateOfBirth: any;
}

interface Dog extends Animal {
    breed: any;
}

class AnimalHouse {
    resident: Animal;
    constructor(animal: Animal) {
        this.resident = animal;
    }
}

class DogHouse extends AnimalHouse {
    // Does not emit JavaScript code,
    // only ensures the types are correct
    declare resident: Dog; // 这里需要declare进行声明
    constructor(dog: Dog) {
        super(dog);
    }
}

JavaScript定义的类初始化顺序是

基类字段被初始化
基类构造函数运行
派生类字段被初始化
派生类构造函数运行

    class Base {
      name = "base";
      constructor() {
        console.log("My name is " + this.name); // base
      }
    }

    class Derived extends Base {
      name = "derived";
    }

    // Prints "base", not "derived"
    const d = new Derived();

权限修饰符

子类继承父类,他是可以修改父类的权限修饰符。只对public,protected修饰符有效。

    class Base {
        protected m = 10;
    }

    class Derived extends Base {
        // No modifier, so default is 'public'
        m = 15;
    }

    const d = new Derived();
    console.log(d.m); // OK

protected修饰符

该修饰符只能在当前类和他的子类中访问,即使他的父类中有该属性,也是不能访问的。 但是这个在java语言中是可以的。

    class Base {
      protected x: number = 1;
    }
    class Derived1 extends Base {
      protected x: number = 5;
    }
    class Derived2 extends Base {
      f1(other: Derived2) {
        other.x = 10;
      }
      f2(other: Derived1) {
        other.x = 10; // Property 'x' is protected and only accessible within class 'Derived1' and its subclasses.
      }
      f3(other: Base) {
        other.x = 10; // Property 'x' is protected and only accessible through an instance of class 'Derived2'. This is an instance of class 'Base'.
      }
    }

private修饰符

他可以跨实例相互访问,但是在ruby语言中不行。

class A {
    private x = 10;
    public sameAs(other: A) {
        // No error
        return other.x === this.x;
    }
}

该私有只是在类型检测期间生效,其实还是可以通过[]来访问对应的私有变量的。

    class MySafe {
      private secretKey = 12345;
      private say() {


      }
    }

    const s = new MySafe();

    // Not allowed during type checking
    console.log(s.secretKey);

    // OK
    console.log(s["secretKey"]);
    console.log(s["say"]());

函数可以被new 调用, 所以一些内容不能设置为静态

函数属性如name、length和call不能定义为静态成员。

为啥静态成员不能使用泛型变量

因为静态成员是随类的加载而加载的,但是静态内容一经加载是不变化的,但是泛型会随着创建的对象不同而变化,这样就出现了错误。

保证在运行时不改变this指向的代价

使用箭头函数时


class MyClass {
    name = "MyClass";
    getName = () => {
        return this.name;
    };
}
const c = new MyClass();
const g = c.getName;
// Prints "MyClass" instead of crashing
console.log(g());
  • 这将使用更多内存,因为每个类实例都有这样定义的每个函数的副本,并没有放在原型上。
  • 你不能用super.的形式调用派生类中的getName,因为原型链中没有获取基类方法的引用。

由于以上的原因,我们可以再定义方法的时候形式的传入指定this的类型,这样就避免了this的改变。这段ts代码编译成js时将删除this参数。

    class MyClass {
      name = "MyClass";
      getName(this: MyClass) { // 显示的指定this类型
        return this.name;
      }
    }
    const c = new MyClass();
    // OK
    c.getName();

    // Error, would crash
    const g = c.getName;
    console.log(g()); // error

编译后

    "use strict";
    // @errors: 2684
    class MyClass {
        constructor() {
            this.name = "MyClass";
        }
        getName() {
            return this.name;
        }
    }
    const c = new MyClass();
    // OK
    c.getName();
    // Error, would crash
    const g = c.getName;
    console.log(g()); // error

this也可以作为一个类型去使用

在类中,一种称为this的特殊类型动态引用当前类的类型。这个this类型会随着当前环境的变化而动态改变。


class Box {
    content: string = "";
    sameAs(other: this) {
        return other.content === this.content;
    }
}

隐式决定类直接的关系

具有相同属性的类,可以相互赋值。表示他们就是一样的。

    class Point1 {
      x = 0;
      y = 0;
    }

    class Point2 {
      x = 0;
      y = 0;
    }

    // OK
    const p: Point1 = new Point2();

具有包含关系的属性类,他们存在继承关系。

    class Person {
      name: string;
      age: number;
    }

    class Employee {
      name: string;
      age: number;
      salary: number;
    }

    // OK
    const p: Person = new Employee();

在结构类型系统中,没有成员的类型通常是其他类型的超类型。所以任何类型都是他的子类型

    class Empty {}

    function fn(x: Empty) {
        // can't do anything with 'x', so I won't
    }

    // All OK!
    fn(window);
    fn({});
    fn(fn);

模块

在编写ts代码时,当前项目如果出现变量名重名,会被提示。我们可以将不同的文件作为单独的模块,这样就避免了错误。

// 在首行声明
export {};

导入类型 import type 来导入类型别名

// @filename: animal.ts
export type Cat = { breed: string; yearOfBirth: number };

// @filename: valid.ts
import type { Cat, Dog } from "./animal.js";
// @filename: app.ts
import { createCatName, type Cat, type Dog } from "./animal.js"; // 导入变量 类型别名

export type Animals = Cat | Dog;
const name = createCatName(); 

export = impport variable = require(path)

这个就相当于module.exports = require()一样。可以统一导出导入。

Awaited<Type>

TypeScript官网内容解读 获得Promise / await的返回值类型。

    import fs = require("fs/promises");
    // const code = fs.readFileSync("a.ts", "utf8");
    (async function() {
      const contents: Awaited<Promise<string>> = await fs.readFile("a.ts", { encoding: 'utf8' });

      console.log("c", contents)
    })()

Partial<Type>

TypeScript官网内容解读

让其类型属性全部变成可选类型。

Required<Type>

TypeScript官网内容解读

让其类型可选属性都变成非可选的。

Readonly<Type>

TypeScript官网内容解读

让其类型的所有属性都变成只读类型。

Record<Keys, Type>

Keys就是字面量的联合类型。

TypeScript官网内容解读

让传入的Keys遍历然后将Type类型赋值给Keys遍历的属性类型。

Pick<Type, Keys>

TypeScript官网内容解读

表示Type类型的过滤(包含)。通过Keys字面量的联合类型去过滤掉Type中的属性类型。并且传入的Keys的类型需要在Type中存在。

Omit<Type, Keys>

这个刚好和Pick<Type, Keys>相反。

TypeScript官网内容解读

表示Type类型的过滤(删除)。通过Keys字面量的联合类型去过滤掉Type中的属性类型。并且传入的Keys的类型需要在Type中存在。

Exclude<UnionType, ExcludedMembers>

TypeScript官网内容解读

通过从UnionType中排除所有可分配给excldedmembers的联合成员来构造一个类型。后者是排除的类型。 并且UnionType必须是联合类型。

    type T2 = Exclude<string | number | (() => void), Function>; // string | number
    
   type T3 = Exclude<{name: string, age: number} | {age: number}, {name: string}> // {age: number}

对于复杂类型的比较,直接包含具有的属性类型即可。就类似于结构匹配。

Extract<Type, Union>

获取交集类型。就是提取Union作为父类型的类型。

    type T1 = Extract<string | number | (() => void), Function>; // () => void

TypeScript官网内容解读

NonNullable<Type>

通过从Type中排除null和undefined来构造类型。

TypeScript官网内容解读

Parameters<Type>

获取Type函数参数类型组成元组类型返回。

TypeScript官网内容解读

    declare function f1(arg: { a: number; b: string }): void;
    type T3 = Parameters<typeof f1>;
    // [arg: { a: number; b: string; }]

ConstructorParameters<Type>

如果Type传入的不是一个函数,那么它将返回never。

TypeScript官网内容解读

ReturnType<Type>

返回函数类型返回值类型。

如果传入的不是函数,那么将返回any。

TypeScript官网内容解读

InstanceType<Type>

由构造函数返回的实例类型组成的类型。

TypeScript官网内容解读

ThisParameterType<Type>

为函数类型提取this形参的类型,如果函数类型没有this形参则为unknown。

TypeScript官网内容解读

OmitThisParameter<Type>

忽略this参数返回,然后妨碍忽略后的类型。

TypeScript官网内容解读

    function toHex(this: Number) {
      return this.toString(16);
    }

    const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);

    console.log(fiveToHex());

ThisType<Type>

使用这个泛型接口时,必须将noImplicitThis开启。

merging interface

接口是可以合并的,当接口声明是一样的时,他会进行合并。

  • 对于非函数属性,类型不同的同名属性会报错。
  • 对于函数属性,会被作为函数重载合并到一起。并且后编写的接口优先级越高。字面量参数的函数属性优先级最高。
    interface Document {

        createElement(tagName: any): Element;

    }

    interface Document {

        createElement(tagName: "div"): HTMLDivElement;

        createElement(tagName: "span"): HTMLSpanElement;

    }

    interface Document {

        createElement(tagName: string): HTMLElement;

        createElement(tagName: "canvas"): HTMLCanvasElement;

    }

    // 合并后
    interface Document {

        createElement(tagName: "canvas"): HTMLCanvasElement;

        createElement(tagName: "div"): HTMLDivElement;

        createElement(tagName: "span"): HTMLSpanElement;

        createElement(tagName: string): HTMLElement;

        createElement(tagName: any): Element;

    }