跳到主要内容

TypeScript内置数据类型

日常的数据类型

ts 是 js 的超集,所以 js 中这些 number、boolean、string、object、bigint、symbol、undefined、null 类型 ts 都是支持的。

除此之外 ts 中新增了这些类型:

Interfaces (接口)

可以描述函数、对象、构造器的结构:

interface Point {
x: number
y: number
}

function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x)
console.log("The coordinate's y value is " + pt.y)
}

interface SayHello {
(name: string): string
}

const func: SayHello = (name: string) => {
return `hello,${name}`
}

我们也可以使用 type(类型别名)来实现上面相同的功能

type Point = {
x: number
y: number
}

// Exactly the same as the earlier example
function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x)
console.log("The coordinate's y value is " + pt.y)
}

可以使用类型别名为任何类型命名,而不仅仅是对象类型。例如,类型别名可以命名联合类型:

type ID = number | string

类型别名和接口非常相似,在许多情况下,可以在它们之间自由选择。几乎所有 interface 的功能都可用 type ,关键的区别在于类型不能重新定义添加新属性,而接口始终是可扩展的。

interface Animal {
name: string
}

interface Bear extends Animal {
honey: boolean
}
// 等同于
type Animal = {
name: string
}

type Bear = Animal & {
honey: boolean
}

const bear = getBear()
bear.name
bear.honey

区别

interface Window {
title: string
}

interface Window {
ts: TypeScriptAPI
}

const src = 'const a = "Hello World"'
window.ts.transpileModule(src, {})
// 类型创建后无法更改
type Window = {
title: string
}

type Window = {
ts: TypeScriptAPI
}

// Error: Duplicate identifier 'Window'.

Enums(枚举)

枚举是一系列值的复合

enum Direction {
Up = 1,
Down,
Left,
Right,
}
const up = Direction.Up // 1

其中 Up 初始化为 1 。从那时起,以下所有成员都将自动递增。换言之, Direction.Up 具有值 1 、 Down 、 2 Left 、 3 和 Right 具有 4 。

Tuple(元组)

元组(Tuple)就是元素个数和类型固定的数组类型:

type Tuple = [number, string]

其他类型

  • never 代表不可达,比如函数抛异常的时候,返回值就是 never,never 可以分配给一切。没有任何东西可以分配给 never。
  • void 对于没有记录返回值的函数。
  • any 是任意类型,任何类型都可以赋值给它,它也可以赋值给任何类型(除了 never)。
  • unknown 是未知类型,任何类型都可以赋值给它,但是它不可以赋值给别的类型。

内置高级数据类型

Partial(部分的)

Partial<Type>构造一个类型,将传入的 Type 属性都设置为可选。返回一个类型,该类型表示给定类型的所有子集。

interface Todo {
title: string
description: string
}

type PartialTodo = Partial<Todo>

const tode1: PartialTodo = {
title: 'Todo Title',
}

const tode2: PartialTodo = {
description: 'Todo description',
}

上面PartialTodo类型里面的所有属性都变成可选类型.

Required(必须的)

Partial相反,Required<Type>将传入的类型属性都设置为必填的.

interface Props {
age?: number
name?: string
}

type RequiredProps = Required<Props>

const RequiredProps: RequiredProps = {
age: 18,
name: 'required',
}

上面RequiredProps类型里面的所有属性都变成必填类型.

Readonly(只读)

Readonly<Type>将传入的类型属性都设置为只读的.

interface Todo {
title: string
}

const todo: Readonly<Todo> = {
title: 'Delete inactive users',
}

todo.title = 'Hello' // 报错
// Cannot assign to 'title' because it is a read-only property.

// 源码
type Readonly<T> = {
readonly [K in keyof T]: T[K]
}

上面ReadonlyTodo类型里面的所有属性都变成只读类型,重新赋值 ts 会提示错误.

Record(记录)

Record<Keys, Type>构造一个属性键为 Keys、属性值为 Type 的对象类型。该工具可用于将一个类型的属性映射到另一个类型。

interface CatInfo {
age: number
breed: string
}

type CatName = 'miffy' | 'boris' | 'mordred'

type catType = Record<CatName, CatInfo>

const cats: catType = {
miffy: { age: 10, breed: 'Persian' },
boris: { age: 5, breed: 'Maine Coon' },
mordred: { age: 16, breed: 'British Shorthair' },
}

新类型catType是一个对象类型他的属性都为CatInfo.通常使用该工具类型构造对象类型.

Pick(选择)

Pick<Type, Keys>通过从 Type 中选取属性 Keys 集来构造类型。

interface Todo {
title: string
description: string
completed: boolean
}

type TodoPreview = Pick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
// 源码
type MyPick<T, K extends keyof T> = {
[P in K]: T[K]
}

这里只选择了Todo类型里面的titlecompleted类型.

Omit(删除)

Omit<Type, Keys>删除 Type 中含有的属性 Keys 集来构造类型。

interface Todo {
title: string
description: string
completed: boolean
createdAt: number
}

type TodoInfo = Omit<Todo, 'completed' | 'createdAt'>

const todoInfo: TodoInfo = {
title: 'Pick up kids',
description: 'Kindergarten closes at 5pm',
}

这里删除了Todo类型里面的completedcreatedAt类型,所以TodoInfo只剩下titledescription类型.

Exclude(排除)

Exclude<T,U>留下两个类型中不同的,类似于差集

type T0 = Exclude<'a' | 'b' | 'c', 'a' | 'c'>
// "b"

type T2 = Exclude<string | number | (() => void), Function>
// string | number

Extract(提取)

Extract<T,U>留下两个类型中相同的,类似于交集

type T0 = Extract<'a' | 'b' | 'c', 'a' | 'c'>
// "a" | "c"

type T2 = Extract<string | number | (() => void), Function>
// () => void
提示

注意Exclude,Extract是使用在联合类型上面的,而PickOmit使用在对象或者 interface 上面的.

NonNullable(不为 null 和 undefined)

NonNullable<Type>通过排除 null 和 undefined 从 Type 来构造类型。

type T1 = NonNullable<boolean | null | undefined>
// boolean

Parameters(参数)/ReturnType(返回类型)

Parameters<Type>ReturnType<Type>可以得到一个函数的参数类型和返回值类型.

declare function f1(a: number, b: string): void
type AddParams = Parameters<typeof f1> // [number, string] 类型
type AddResult = ReturnType<typeof f1> // void 类型

// 源码
type Parameters<T extends (...args: any) => any> = T extends (
...args: infer P
) => any
? P
: never

type ReturnType<T extends (...args: any) => any> = T extends (
...args: any
) => infer R
? R
: any

Awaited(等待)

Awaited<Type>获取异步函数的返回类型.

const promise = new Promise<string>((resolve) => {
resolve('hello')
})

type promiseType = Awaited<typeof promise> // string

字符串操作类型

Uppercase

Uppercase<Type>将字符串中的每个字符转换为大写版本。

type Greeting = 'Hello, world'
type ShoutyGreeting = Uppercase<Greeting> // "HELLO, WORLD"

type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<'my_app'> // ID-MY_APP

Lowercase

Lowercase<Type>将字符串中的每个字符转换为等效的小写字符。

type Greeting = 'Hello, world'
type ShoutyGreeting = Lowercase<Greeting> // "hello, world"

type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`
type MainID = ASCIICacheKey<'MY_APP'> // id-my_app
提示

这里的extends对传入的 Str 做了约束只能是 string 类型.

Capitalize

Capitalize<Type>将字符串中的第一个字符转换为大写等效字符。

type Greeting = 'hello, world'
type ShoutyGreeting = Capitalize<Greeting> // "Hello, world"

Uncapitalize

Uncapitalize<Type>将字符串中的第一个字符转换为等效的小写字符。

type Greeting = 'Hello, world'
type ShoutyGreeting = Uncapitalize<Greeting> // "hello, world"

类型运算

条件类型(Conditional Types)

extends 可以通过两种方式使用

当 extends 左边的类型可以赋值给右边的类型时,返回第一个类型,否则返回第二个类型(:后面的).

type res<T> = T extends string ? string : number

type res1 = res<string> // string
type res2 = res<boolean> // number

分配条件类型

当传入的类型参数为联合类型时,他们会被 分配类型 。 以下面的例子为例:

type ToArray<Type> = Type extends any ? Type[] : never

// 如果我们将联合类型传入 ToArray,则条件类型将应用于该联合类型的每个成员。

type StrArrOrNumArr = ToArray<string | number>

// type StrArrOrNumArr = string[] | number[]

通常,分布性是所需的行为。 要避免这种行为,可以用方括号括起 extends 关键字的两边。

type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never

// 'StrOrNumArr' 不再是一个联合类型
type StrOrNumArr = ToArrayNonDist<string | number>

// type StrOrNumArr = (string | number)[]

举个例子

type A = 'a' | 'b' | 'c'
type B = 'a' | 'd'

type MyExclude<T, U> = T extends U ? never : T
type C = MyExclude<A, B>

// 等同于

type C =
| ('a' extends 'a' | 'd' ? never : 'a')
| ('b' extends 'a' | 'd' ? never : 'b')
| ('c' extends 'a' | 'd' ? never : 'c')

// type C = 'c'

这也是Exclude的实现方式

infer (推导)

提取类型的一部分,示例获取参数类型

type argType<T> = T extends (...args: infer P) => any ? P : never

type Fn = argType<(a: number, b: string) => number>
// type Fn = [a: number, b: string]

联合:|

类型可以是后面几个类型之一,有点类似于或运算

type res = number | string

交叉:&

同一类型可以合并,不同的类型没法合并,会被舍弃:

type Combined = { a: number } & { b: string }
// 此时 Combined 具有两个属性, a 并且 b

type res = number & string
// res = never
// 没有一个类型即是number 也是 string. 所以ts推断为never,表示没有可能的值。

映射类型

如 Partial 源码,将所有类型变成可选类型.

type Partial<T> = {
[Key in keyof T]?: T[Key]
}

type res = Partial<{
a: string
b: number
}>
// type res = {
// a?: string | undefined;
// b?: number | undefined;
// }

keyof: 取 interface 的键后保存为联合类型

interface userInfo {
name: string
age: number
}
type keyofValue = keyof userInfo
// keyofValue = "name" | "age"

T[Key] 是取索引类型某个索引的值,叫做索引访问。

in: 取联合类型的值,主要用于数组和对象的构建

type name = 'firstname' | 'lastname'
type TName = {
[key in name]: string
}
// TName = { firstname: string, lastname: string }

类型挑战

记录一下有些题目需要注意的

元组转换为对象

type TupleToObject<T extends readonly (string | number | symbol)[]> = {
[P in T[number]]: P
}
  • T[number] 来提取元组 T 中所有元素的联合类型.
  • [P in T[number]] 迭代这个联合类型中的每个元素 P
  • P 同时被用作结果对象类型中的键和值。 这里因为对象键只可能是 string number symbol,所以也可以使用内置类型 PropertyKey
type TupleToObject<T extends readonly PropertyKey[]> = {
[P in T[number]]: P
}
// PropertyKey 类型定义是 declare type PropertyKey = string | number | symbol;

concat

展开运算符 ... 将 T 和 U 展开,并将它们合并成一个新的元组类型。

type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U]