TypeScript Note

qzda (2023/08/16)

# any & unknown

any 类型除了关闭类型检查,还会污染其他具有正确类型的变量。

let x: any = "hello";
let y: number;

y = x; // 不报错
y * 123; // 不报错,实际为 `"hello" * 123`
let x: any = "hello";
let y: number;

y = x; // 不报错
y * 123; // 不报错,实际为 `"hello" * 123`

为解决污染问题,TypeScript 3.0 引入了unknown类型,可以视为严格版的any

unknownany基本一致,可被赋值任意类型,但不能直接赋值给其他类型的变量(除anyunknown类型)。

# 装饰器

装饰器提供了一种添加元数据、修改行为、验证或扩展目标元素功能的机制。它们是在运行时执行的函数。多个装饰器可以应用于一个声明。

# 类装饰器

扩展现有类,例如添加属性或方法、收集类的实例。

type Constructor<T = {}> = new (...args: any[]) => T;

function toCustomString<Class extends Constructor>(
  Value: Class,
  context: ClassDecoratorContext<Class>
) {
  return class extends Value {
    constructor(...args: any[]) {
      super(...args);
      console.log(JSON.stringify(this));
      console.log(JSON.stringify(context));
    }
  };
}

@toCustomString
class Person {
  name: string;

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

  greet() {
    return "Hello, " + this.name;
  }
}
const person = new Person("qzda");
/* Logs:
{"name":"qzda"}
{"kind":"class","name":"Person",}
*/
type Constructor<T = {}> = new (...args: any[]) => T;

function toCustomString<Class extends Constructor>(
  Value: Class,
  context: ClassDecoratorContext<Class>
) {
  return class extends Value {
    constructor(...args: any[]) {
      super(...args);
      console.log(JSON.stringify(this));
      console.log(JSON.stringify(context));
    }
  };
}

@toCustomString
class Person {
  name: string;

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

  greet() {
    return "Hello, " + this.name;
  }
}
const person = new Person("qzda");
/* Logs:
{"name":"qzda"}
{"kind":"class","name":"Person",}
*/

# 属性装饰器

修改属性的行为,例如更改初始化值。

function upperCase<T>(
  target: undefined,
  context: ClassFieldDecoratorContext<T, string>
) {
  return function (this: T, value: string) {
    return value.toUpperCase();
  };
}

class MyClass {
  @upperCase
  prop1 = "hello!";
}

console.log(new MyClass().prop1); // Logs: HELLO!
function upperCase<T>(
  target: undefined,
  context: ClassFieldDecoratorContext<T, string>
) {
  return function (this: T, value: string) {
    return value.toUpperCase();
  };
}

class MyClass {
  @upperCase
  prop1 = "hello!";
}

console.log(new MyClass().prop1); // Logs: HELLO!

# 方法装饰器

更改或增强方法的行为。

function log<This, Args extends any[], Return>(
  target: (this: This, ...args: Args) => Return,
  context: ClassMethodDecoratorContext<
    This,
    (this: This, ...args: Args) => Return
  >
) {
  const methodName = String(context.name);

  function replacementMethod(this: This, ...args: Args): Return {
    console.log(`LOG: Entering method '${methodName}'.`);
    const result = target.call(this, ...args);
    console.log(`LOG: Exiting method '${methodName}'.`);
    return result;
  }

  return replacementMethod;
}

class MyClass {
  @log
  sayHello() {
    console.log("Hello!");
  }
}

console.log(new MyClass().sayHello()); // Logs: Hello!
function log<This, Args extends any[], Return>(
  target: (this: This, ...args: Args) => Return,
  context: ClassMethodDecoratorContext<
    This,
    (this: This, ...args: Args) => Return
  >
) {
  const methodName = String(context.name);

  function replacementMethod(this: This, ...args: Args): Return {
    console.log(`LOG: Entering method '${methodName}'.`);
    const result = target.call(this, ...args);
    console.log(`LOG: Exiting method '${methodName}'.`);
    return result;
  }

  return replacementMethod;
}

class MyClass {
  @log
  sayHello() {
    console.log("Hello!");
  }
}

console.log(new MyClass().sayHello()); // Logs: Hello!

# 工具类型

常用的内置工具来操作类型。

# Awaited<T>

构造一个递归解包 Promise 的类型。

type A = Awaited<Promise<string>>; // string
type A = Awaited<Promise<string>>; // string

# Partial<T> & Required<T>

构造一个类型,并将 T 的所有属性设置为可选/必选。

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

// { name?: string | undefined; age?: number | undefined; }
type A = Partial<Person>;
// { name: string; age: number; }
type B = Required<A>;
type Person = {
  name: string;
  age: number;
};

// { name?: string | undefined; age?: number | undefined; }
type A = Partial<Person>;
// { name: string; age: number; }
type B = Required<A>;

# Readonly<T>

构造一个类型,并将 T 的所有属性设置为只读。

# Record<K, T>

构造一个具有类型 T 的一组属性 K 的类型。

# Pick<T, K>

通过从 T 中选取指定属性 K 来构造类型。

# Omit<T, K>

通过从 T 中省略指定属性 K 来构造类型。

type Product = {
  name: string;
  price: number;
};

// { name: string; }
type Name = Omit<Product, "price">;
type Product = {
  name: string;
  price: number;
};

// { name: string; }
type Name = Omit<Product, "price">;

# Exclude<T, U>

通过从 T 中排除类型 U 的所有值来构造类型。

type Union = "a" | "b" | "c";
type MyType = Exclude<Union, "a" | "c">; // b
type Union = "a" | "b" | "c";
type MyType = Exclude<Union, "a" | "c">; // b

# Extract<T, U>

通过从 T 中提取类型 U 的所有值来构造类型。

type Union = "a" | "b" | "c";
type MyType = Extract<Union, "a" | "c">; // a | c
type Union = "a" | "b" | "c";
type MyType = Extract<Union, "a" | "c">; // a | c

# NonNullable<T>

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

type Union = "a" | null | undefined | "b";
type MyType = NonNullable<Union>; // 'a' | 'b'
type Union = "a" | null | undefined | "b";
type MyType = NonNullable<Union>; // 'a' | 'b'

# Parameters<T>

提取函数类型 T 的参数类型。

type Func = (a: string, b: number) => void;
type MyType = Parameters<Func>; // [a: string, b: number]
type Func = (a: string, b: number) => void;
type MyType = Parameters<Func>; // [a: string, b: number]

# ConstructorParameters<T>

提取构造函数类型 T 的参数类型。

class Person {
  constructor(
    public name: string,
    public age: number
  ) {}
}

// [name: string, age: number]
type PersonConstructorParams = ConstructorParameters<typeof Person>;
const params: PersonConstructorParams = ["John", 30];
const person = new Person(...params);
// Person { name: 'John', age: 30 }
console.log(person);
class Person {
  constructor(
    public name: string,
    public age: number
  ) {}
}

// [name: string, age: number]
type PersonConstructorParams = ConstructorParameters<typeof Person>;
const params: PersonConstructorParams = ["John", 30];
const person = new Person(...params);
// Person { name: 'John', age: 30 }
console.log(person);

# ReturnType<T>

提取函数类型 T 的返回类型。

type Func = (name: string) => number;
// number
type MyType = ReturnType<Func>;
type Func = (name: string) => number;
// number
type MyType = ReturnType<Func>;

# InstanceType<T>

提取类类型 T 的实例类型。

class Person {
  name: string;

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

  sayHello() {
    console.log(`Hello, my name is ${this.name}!`);
  }
}

type PersonInstance = InstanceType<typeof Person>;

const person: PersonInstance = new Person("John");

// Hello, my name is John!
person.sayHello();
class Person {
  name: string;

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

  sayHello() {
    console.log(`Hello, my name is ${this.name}!`);
  }
}

type PersonInstance = InstanceType<typeof Person>;

const person: PersonInstance = new Person("John");

// Hello, my name is John!
person.sayHello();

# ThisParameterType<T>

从函数类型 T 中提取“this”参数的类型。

interface Person {
  name: string;
  greet(this: Person): void;
}
// Person
type PersonThisType = ThisParameterType<Person["greet"]>;
interface Person {
  name: string;
  greet(this: Person): void;
}
// Person
type PersonThisType = ThisParameterType<Person["greet"]>;

# OmitThisParameter<T>

从函数类型 T 中删除“this”参数。

function capitalize(this: String) {
  return this[0].toUpperCase + this.substring(1).toLowerCase();
}
// () => string
type CapitalizeType = OmitThisParameter<typeof capitalize>;
function capitalize(this: String) {
  return this[0].toUpperCase + this.substring(1).toLowerCase();
}
// () => string
type CapitalizeType = OmitThisParameter<typeof capitalize>;

# ThisType<T>

作为上下文类型 this 的一部分。

type Logger = {
  log: (error: string) => void;
};

let helperFunctions: { [name: string]: Function } & ThisType<Logger> = {
  hello: function () {
    this.log("some error"); // 有效,因为“log”是“this”的一部分
    this.update(); // 无效
  },
};
type Logger = {
  log: (error: string) => void;
};

let helperFunctions: { [name: string]: Function } & ThisType<Logger> = {
  hello: function () {
    this.log("some error"); // 有效,因为“log”是“this”的一部分
    this.update(); // 无效
  },
};

# Uppercase<T> & Lowercase<T>

将输入类型 T 的名称设为大/小写。

type MyType = Uppercase<"abc">; // "ABC"
type MyType = Lowercase<"ABC">; // "abc"
type MyType = Uppercase<"abc">; // "ABC"
type MyType = Lowercase<"ABC">; // "abc"

# Capitalize<T> & Uncapitalize<T>

将输入类型 T 的名称大写/取消大写。

type MyType = Capitalize<"abc">; // "Abc"
type MyType = Uncapitalize<"Abc">; // "abc"
type MyType = Capitalize<"abc">; // "Abc"
type MyType = Uncapitalize<"Abc">; // "abc"
topback