TypeScript中的内置类型

2021/7/22 TypeScript

术语解释:

  • extends:条件类型,和class中的继承是两码事。
  • keyof:与Object.keys相似,keyofinterfacetype的键,是索引类型操作符。
  • in:检测属性是否在对象中。
  • infer:表示在extends条件语句中待推断的类型变量,用于「替换手动获取类型」。
  • &:交叉类型。
  • |:联合类型。
  • ?:可选属性。
  • -?:去掉可选,变成必选属性。
  • readonly:只读属性。

# Partial

  • Partial<T>将类型T的所有属性标记为可选属性。
type User = {
  name: string;
  age: number;
}
type PartialUser = Partial<User>
// 等效于
type PartialUser2 = {
  name?: string;
  age?: number;
}

// 源码实现
type Partial<T> = { [P in keyof T]?: T[P]; }
// keyof 取到 T 中的属性名,in 操作符遍历属性名。
// 可选属性操作符 ? 将属性定义为可选属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Required

  • Required<T>将类型T的所有属性标记为必选属性。
type User = {
  name?: string;
  age?: number;
}
type RequiredUser = Required<User>
// 等效于
type RequiredUser2 = {
  name: string;
  age: number;
}

// 源码实现
type Required<T> = { [P in keyof T]-?: T[P]; }
1
2
3
4
5
6
7
8
9
10
11
12
13

# Readonly

  • Readonly<T>将类型T的所有属性标记为readonly属性,即不能修改。
type User = {
  name: string;
  age?: number;
}
type ReadonlyUser = Readonly<User>
// 等效于
type ReadonlyUser2 = {
  readonly name: string;
  readonly age?: number;
}

const user: ReadonlyUser = { name: '123', age: 21 }
user.name = 'xxx' // 无法分配到 "name" ,因为它是只读属性

// 源码实现
type Readonly<T> = { readonly [P in keyof T]: T[P]; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Record

  • Record<K, T>构造一个类型,该类型具有一组属性K,每个属性的类型为T。可用于将一个类型的属性映射为另一个类型。
type keys = 'name' | 'age'
type User = Record<keys, string>
// 等效于
type User1 = {
  name: string;
  age: string;
}

// 源码实现
type ToRecord<K extends keyof any, T> = {
  [P in K]: T;
};
1
2
3
4
5
6
7
8
9
10
11
12

# Pick

  • Pick<T, K>T中抽取一组属性K构建一个新类型。
type Todo = {
  title: string;
  description: string;
  completed: boolean;
}
type preview = Pick<Todo, 'title' | 'description'>
// 等效于
type preview1 = {
  title: string;
  description: string;
}

// 源码实现
type ToPick<T, K extends keyof T> = {
  [P in K]: T[P];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Omit

  • Omit<T, K>T中去除一组属性K构建一个新类型。
type Todo = {
  title: string;
  description: string;
  completed: boolean;
}
type preview = Omit<Todo, 'completed' | 'description'>
// 等效于
type preview1 = {
  title: string;
}

// 源码实现 结合Exclude
type Omit<T, K extends keyof any> = {
  [P in Exclude<keyof T, K>]: T[P]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Extract

  • Extract<T, U>提取T中可以赋值给U的类型(即提取TU的交集)。
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
// type T0 = "a"
type T1 = Extract<string | number | (() => void), Function>;
// type T1 = () => void

// 源码实现
type Extract<T, U> = T extends U ? T : never;
1
2
3
4
5
6
7

# Exclude

  • Exclude<T, U>T中剔除可以赋值给U的类型(返回T中除了U的类型)。
type T0 = Exclude<"a" | "b" | "c", "a">;
// type T0 = "b" | "c"; // 去掉a
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// type T1 = "c"; // 去掉a,b
type T2 = Exclude<string | number | (() => void), Function>;
// type T2 = string | number; // 去掉function

// 源码实现
type Exclude<T, U> = T extends U ? never : T;
1
2
3
4
5
6
7
8
9

TIPS

  1. Pick<T, K>Omit<T, K>为一组,都是对含键值的类型或接口进行操作,参数T必须是有键值对的,参数K是联合类型。
  2. Extract<T, U>Exclude<T, U>为一组,都是对联合类型就行操作,两个参数都为联合类型。

# NonNullable

  • NonNullable<T>T中剔除nullundefined
type T0 = NonNullable<string | number | undefined>;
// type T0 = string | number
type T1 = NonNullable<string[] | null | undefined>;
// type T1 = string[]

// 源码实现 只用继承自null或者undefined就行
type NonNullable<T> = T extends null | undefined ? never : T; // 4.8版本之前
type NonNullable<T> = T & {}; // 4.8版本之后
1
2
3
4
5
6
7
8

# Parameters

  • Parameters<T>返回类型为T的函数的参数类型所组成的元组。
type T0 = Parameters<() => string>;
// type T0 = []
type T1 = Parameters<(s: string) => void>;
// type T1 = [s: string]
function f1(arg: { a: number; b: string }): void {}
type T2 = Parameters<typeof f1>;
// type T2 = [arg: {
//   a: number;
//   b: string;
// }]

// 源码实现
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never
1
2
3
4
5
6
7
8
9
10
11
12
13

# ConstructorParameters

  • ConstructorParameters<T>返回T构造函数的参数类型所组成的元组,如果T不是函数则为nerver
interface Person {
  new(name: string, age: number): void;
  name: string;
  age: number;
}

type T0 = ConstructorParameters<Person>;
// type T0 = [name: string, age: number]

// 源码实现
type ConstructorParameters<T extends abstract new (...args: any) => any> = T extends abstract new (...args: infer P) => any ? P : never
1
2
3
4
5
6
7
8
9
10
11

# ReturnType

  • ReturnType<T>获取函数类型T的返回值类型。
type T0 = ReturnType<() => string>;
// type T0 = string
type T1 = ReturnType<(s: string) => void>;
// type T1 = void
declare function f1(): { a: number; b: string };
type T2 = ReturnType<typeof f1>;
// type T2 = {
//   a: number;
//   b: string;
// }

// 源码实现
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any
1
2
3
4
5
6
7
8
9
10
11
12
13

# InstanceType

  • InstanceType<T>获取构造函数类型的实例类型。
class C {
  x = 0;

  y = 0;
}

type T0 = InstanceType<typeof C>;
// type T0 = C

// 源码实现
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any
1
2
3
4
5
6
7
8
9
10
11

# ThisParameterType

  • ThisParameterType<T>只提取函数类型T中的this参数类型,如果没有this参数则为unknown
function test(this: string[], name: string): string[] {
  this.push(name);
  return this;
}

type T0 = ThisParameterType<typeof test>
// type T0 = string[]

// 源码实现
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown
1
2
3
4
5
6
7
8
9
10

# OmitThisParameter

  • OmitThisParameter<T>删除函数类型T中的this参数,如果没有显示定义this这个参数,那么就是一个简单的类型。
function toHex(this: Number) {
  return this.toString(16);
}
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
// fiveToHex: () => string

// 源码实现
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T
1
2
3
4
5
6
7
8

# ThisType

原文地址 (opens new window)

  • ThisType<T>可以在对象字面量中键入this,并提供通过上下文类型控制this类型的便捷方式。它只有在--noImplicitThis的选项下才有效。

现在,在对象字面量方法中的this类型,将由以下决定:

  • 如果这个方法显式指定了this参数,那么this具有该参数的类型。(下例子中bar)
  • 否则,如果方法由带this参数的签名进行上下文键入,那么this具有该参数的类型。(下例子中foo)
  • 否则,如果--noImplicitThis选项已经启用,并且对象字面量中包含由ThisType<T>键入的上下文类型,那么this的类型为T
  • 否则,如果--noImplicitThis选项已经启用,并且对象字面量中不包含由ThisType<T>键入的上下文类型,那么this的类型为该上下文类型。
  • 否则,如果--noImplicitThis选项已经启用,this具有该对象字面量的类型。
  • 否则,this的类型为any
// Compile with --noImplicitThis

type Point = {
  x: number;
  y: number;
  moveBy(dx: number, dy: number): void;
};

let p: Point = {
  x: 10,
  y: 20,
  moveBy(dx, dy) {
    this.x += dx; // this has type Point
    this.y += dy; // this has type Point
  }
};

let foo = {
  x: 'hello',
  f(n: number) {
    this; // { x: string, f(n: number): void }
  }
};

let bar = {
  x: 'hello',
  f(this: { message: string }) {
    this; // { message: string }
  }
};
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

# 内在字符串操作类型

# Uppercase

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

# Lowercase

  • Lowercase<StringType>将字符串中的每个字符转换为小写版本。
type Greeting = "Hello, world"
type QuietGreeting = Lowercase<Greeting>
// type QuietGreeting = "hello, world"
1
2
3

# Capitalize

  • Capitalize<StringType>首字母大写,其他不变。
type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>;
// type Greeting = "Hello, world"
1
2
3

# Uncapitalize

  • Uncapitalize<StringType>首字母小写,其他不变。
type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;
// type UncomfortableGreeting = "hELLO WORLD"
1
2
3
最近更新: 2024年09月27日 17:56:19