Skip to content

概念

TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程

优势

  • 解决大型项目的稳定性, 可维护性

  • 强类型, 支持静态和动态类型

  • 编译期间发现错误

基础类型

  • boolean

  • number

  • string

  • undefined

  • null

undefined 和 null 默认是所有类型的子类型

指定 strictNullChecks 之后, 只能只能赋值给自己和 void

  • any

  • unknown

安全类型, 类似 any, unknown 类型 无法赋值给其他类型变量

  • void

  • never

永远不存在的类型

程序无法运行到(异常)

  • symbol

  • 数组类型

  • 元组类型

函数类型

interface

interface 是 TS 设计出来用于定义对象类型的,可以对对象的形状进行描述

可选属性

readonly 只读属性

自定义属性(可索引的类型)

枚举

高级类型

联合类型

交叉类型

类型别名

与 interface 对比

  • 相同点

都可以定义一个对象或函数

都允许继承, interface 使用 extends,type 使用交叉类型实现继承

  • 区别

类型别名用于给各种类型定义别名, 更加简洁

type 可以声明基本类型, 联合类型, 交叉类型, 元组, interface 不行

interface 可以合并重复声明

索引类型

keyof 取某个类型的键, 返回联合类型

泛型约束

extends, 约束泛型变量

映射类型

in

对联合类型遍历

泛型

软件工程中,我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能

体操

Await

Typescript
type MyAwaited<T> = T extends Promise<infer K> ? (K extends Promise<unknown> ? MyAwaited<K> : K) : T

Pick

Typescript
type MyPick<T, K extends keyof T> = { [P in K]: T[P] }

Exclude

Typescript
type MyExclude<T, K extends T> = T extends K ? never : T

First

Typescript
type First<T extends unknown[]> = T extends [infer P, ...unknown[]] ? P : never

Readonly

Typescript
type MyReadonly<T> = { readonly [K in keyof T]: T[K] }

IF

Typescript
type If<C extends boolean, T, F> = C extends true ? T : F

Concat

Typescript
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]

Push

Typescript
type Push<T extends unknown[], U> = [...T, U]

Includes

Typescript
type Includes<T extends unknown[], U> = T extends [infer P, ...infer K]
  ? Equal<P, U> extends true
    ? true
    : Includes<K, U>
  : false

Equal

Typescript
type Equal<P, U> = (<T>() => T extends P ? 1 : 2) extends <T>() => T extends U ? 1 : 2 ? true : false

Unshift

Typescript
type Unshift<T extends unknown[], U> = [U, ...T]

Parameters

Typescript
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer K) => any ? K : never

Readonly2

Typescript
type MyReadonly2<T, K extends keyof T = keyof T> = { readonly [R in keyof T]: T[R] } & {
  [R in Exclude<keyof T, K>]: T[R]
}

Chainable

Typescript
type Chainable<T = {}> = {
  option: <K extends string, V>(
    key: K extends keyof T ? (V extends T[K] ? never : K) : K,
    value: V
  ) => Chainable<Omit<T, K> & { [P in K]: V }>
  get: () => T
}

DeepReadonly

Typescript
type DeepReadonly<T> = T extends Object
  ? {
      readonly [R in keyof T]: T[R] extends Object
        ? T[R] extends Function
          ? T[R]
          : DeepReadonly<T[R]>
        : Readonly<T[R]>
    }
  : never

TupleToUnion

Typescript
type TupleToUnion<T extends unknown[]> = T[number]

Last

Typescript
type Last<T extends unknown[]> = T extends [...unknown[], infer R] ? R : T[0]

PromiseAll

Typescript
declare function PromiseAll<T extends unknown[]>(
  values: readonly [...T]
): Promise<{ [R in keyof T]: T[R] extends Promise<infer P> ? Awaited<P> : T[R] }>

Lookup

Typescript
type Lookup<U, T extends string> = U extends { type: T } ? U : never

Capitalize

Typescript
type MyCapitalize<S extends string> = S extends `${infer P}${infer R}` ? `${Uppercase<P>}${R}` : S

Typescript
// 1 ways
type UpperLetter =
  | 'A'
  | 'B'
  | 'C'
  | 'D'
  | 'E'
  | 'F'
  | 'G'
  | 'H'
  | 'I'
  | 'J'
  | 'K'
  | 'L'
  | 'M'
  | 'N'
  | 'O'
  | 'P'
  | 'Q'
  | 'R'
  | 'S'
  | 'T'
  | 'U'
  | 'V'
  | 'W'
  | 'X'
  | 'Y'
  | 'Z'

type KebabCase<S extends string, U = S> = S extends `${infer R}${infer P}`
  ? R extends UpperLetter
    ? U extends `${infer _}${infer US}`
      ? P extends US
        ? `${Lowercase<R>}${KebabCase<P, S>}`
        : `-${Lowercase<R>}${KebabCase<P, S>}`
      : S
    : `${R}${KebabCase<P, S>}`
  : S

// 2 ways
type KebabCase<S extends string> = S extends `${infer R}${infer P}`
  ? P extends Uncapitalize<P>
    ? `${Lowercase<R>}${KebabCase<P>}`
    : `${Lowercase<R>}-${KebabCase<P>}`
  : S

AnyOf

Typescript
// 1 way
type AnyOf<T extends unknown[]> = T extends [infer R, ...infer P]
  ? R extends [] | '' | false | 0 | Record<string, never>
    ? AnyOf<P>
    : true
  : false

// 2 way
type AnyOf<T extends any[]> = T[number] extends 0 | '' | false | [] | { [key: string]: never } ? false : true

IsNever

Typescript
type IsNever<T> = [T] extends [never] ? true : false

Replace

Typescript
type Replace<S extends string, From extends string, To extends string> = From extends ''
  ? S
  : S extends `${infer R}${From}${infer P}`
  ? `${R}${To}${P}`
  : S

ReplaceAll

Typescript
type ReplaceAll<S extends string, From extends string, To extends string> = From extends ''
  ? S
  : S extends `${infer R}${From}${infer P}`
  ? `${ReplaceAll<R, From, To>}${To}${ReplaceAll<P, From, To>}`
  : S

Parameters

Typescript
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer R) => any ? R : never

Flatten

Typescript
type Flatten<T extends unknown[]> = T extends [infer R, ...infer P]
  ? R extends unknown[]
    ? [...Flatten<R>, ...Flatten<P>]
    : [R, ...Flatten<P>]
  : []

Absolute

Typescript
type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer R}` ? R : `${T}`

IsUnion

Typescript
type IsUnion<T, P = T> = [T] extends [never] ? false : T extends P ? ([P] extends [T] ? false : true) : false

ReplaceKeys

Typescript
type ReplaceKeys<U, T, Y> = U extends U
  ? { [R in keyof U]: R extends T ? (R extends keyof Y ? Y[R] : never) : U[R] }
  : never

PickByType

Typescript
type PickByType<T, U> = { [R in keyof T as T[R] extends U ? R : never]: T[R] }

OmitByType

Typescript
type OmitByType<T, U> = { [R in keyof T as T[R] extends U ? never : R]: T[R] }

StartsWith

Typescript
type StartsWith<T extends string, U extends string> = T extends `${U}${infer _}` ? true : false

EndsWith

Typescript
type EndsWith<T extends string, U extends string> = T extends `${infer _}${U}` ? true : false

PartialByKeys

Typescript
type Merge<T> = {
  [Key in keyof T]: T[Key]
}
type PartialByKeys<T, K = keyof T> = Merge<
  { [R in keyof T as R extends K ? never : R]: T[R] } & { [R in keyof T as R extends K ? R : never]?: T[R] }
>

RequiredByKeys

Typescript
type Merge<T> = { [R in keyof T]: T[R] }
type RequiredByKeys<T, K = keyof T> = Merge<
  { [R in keyof T as R extends K ? never : R]: T[R] } & { [R in keyof T as R extends K ? R : never]-?: T[R] }
>

IndexOf

Typescript
type IndexOf<T, U, Res extends unknown[] = []> = T extends [infer P, ...infer R]
  ? Equal<U, P> extends true
    ? Res['length']
    : IndexOf<R, U, [...Res, P]>
  : -1

LastIndexOf

Typescript
type LastIndexOf<T extends unknown[], U> = T extends [...infer R, infer P]
  ? Equal<P, U> extends true
    ? R['length']
    : LastIndexOf<R, U>
  : -1

Unique

Typescript
type IndexOf<T, U, Res extends unknown[] = []> = T extends [infer P, ...infer R]
  ? Equal<U, P> extends true
    ? Res['length']
    : IndexOf<R, U, [...Res, P]>
  : -1

type Unique<T, Res extends unknown[] = []> = T extends [infer P, ...infer R]
  ? IndexOf<Res, P> extends -1
    ? Unique<R, [...Res, P]>
    : Unique<R, Res>
  : Res

MapTypes

Typescript
type Include<T, U> = T extends { mapFrom: any; mapTo: any }
  ? Equal<T['mapFrom'], U> extends true
    ? T['mapTo']
    : never
  : never

type MapTypes<T, R extends { mapFrom: any; mapTo: any } | { mapFrom: any; mapTo: any }> = {
  [P in keyof T]: Include<R, T[P]> extends never ? T[P] : Include<R, T[P]>

Shift

Typescript
type Shift<T> = T extends [infer _, ...infer R] ? R : never

Reverse

Typescript
type Reverse<T> = T extends [...infer R, infer K] ? [K, ...Reverse<R>] : []

TupleToNestedObject

Typescript
type TupleToNestedObject<T, U> = T extends [infer R, ...infer Rest]
  ? R extends string
    ? { [K in R]: TupleToNestedObject<Rest, U> }
    : U
  : U

FlipArguments

Typescript
type Reverse<T> = T extends [...infer R, infer K] ? [K, ...Reverse<R>] : []
type FlipArguments<T extends (...args: any) => any> = T extends (...args: infer R) => infer P
  ? (...args: Reverse<R>) => P
  : never

Flip

Typescript
type Flip<T extends Record<string | number | symbol, any>> = {
  [R in keyof T as T[R] extends boolean ? `${T[R]}` : R extends string | number | symbol ? T[R] : never]: R
}

Subsequence

Typescript
type Subsequence<T extends any[]> = T extends [infer P, ...infer R] ? [...Subsequence<R>] | [P, ...Subsequence<R>] : []

Without

Typescript
// 1 way
type TupleToUnion<T> = T extends unknown[] ? T[number] : T
type Without<T, U, K = TupleToUnion<U>> = T extends [infer P, ...infer R]
  ? P extends K
    ? Without<R, U>
    : [P, ...Without<R, U>]
  : []

// 2 way
type IndexOf<T, U, Res extends unknown[] = []> = T extends [infer P, ...infer R]
  ? Equal<U, P> extends true
    ? Res['length']
    : IndexOf<R, U, [...Res, P]>
  : -1
type Exist<T, U> = T extends unknown[] ? (IndexOf<T, U> extends -1 ? false : true) : Equal<T, U>
type Without<T, U> = T extends [infer P, ...infer R]
  ? Exist<U, P> extends false
    ? [P, ...Without<R, U>]
    : Without<R, U>
  : []

Zip

Typescript
type Zip<T, U> = T extends [infer P, ...infer R]
  ? U extends [infer K, ...infer KR]
    ? [[P, K], ...Zip<R, KR>]
    : []
  : []

Fibonacci

Typescript
type Fibonacci<
  T extends number,
  U extends unknown[] = [0],
  U1 extends unknown[] = [0],
  U2 extends unknown[] = [0],
> = U['length'] extends T ? U1['length'] : Fibonacci<T, [...U, 0], U2, [...U1, ...U2]>

Chunk

Typescript
type Chunk<T, U, K extends unknown[] = []> = K['length'] extends U
  ? [K, ...Chunk<T, U, []>]
  : T extends [infer P, ...infer R]
  ? Chunk<R, U, [...K, P]>
  : K['length'] extends 0
  ? []
  : [K]

Split

Typescript
type Split<S extends string, SEP extends string, Res extends string[] = []> = Equal<string, S> extends true
  ? string[]
  : S extends `${infer P}${SEP}${infer R}`
  ? Split<R, SEP, [...Res, P]>
  : SEP extends ''
  ? [...Res]
  : [...Res, S]

LengthOfString

Typescript
type LengthOfString<S extends string, U extends string[] = []> = S extends `${infer P}${infer R}`
  ? LengthOfString<R, [...U, P]>
  : U['length']

RequiredKeys

Typescript
type RequiredKeys<T> = keyof { [R in keyof T as T[R] extends Required<T>[R] ? R : never]: never }

Assign

Typescript
type Assign<T extends Record<string, unknown>, U extends unknown[]> = U extends [infer P, ...infer R]
  ? Assign<Merge<T, P>, R>
  : T

TypedGet

Typescript
type GetKey<T extends unknown[] | Record<string, any>, K extends string> = T extends unknown[]
  ? T[0] extends Record<string, unknown>
    ? T[0][K]
    : never
  : T extends Record<string, any>
  ? T[K]
  : never

type Get<O extends Record<string, any>, T extends string> = unknown extends O[T]
  ? T extends `${infer K}.${infer R}`
    ? R extends string
      ? O[R] extends unknown
        ? Get<GetKey<O, K>, R>
        : O[R]
      : GetKey<O, K>
    : T extends string
    ? GetKey<O, T>
    : never
  : O[T]

Released under the MIT License.