Skip to content

Typescript 高级类型 ​

交叉类型 ​

使用 & 合并多个类型:

ts
const mergeFunc = <T, U>(arg1: T, arg2: U): T & U => {
  let res = {} as T & U
  res = Object.assign(arg1, arg2)
  return res
}
mergeFunc({ a: 'a' }, { b: 'b' })
const mergeFunc = <T, U>(arg1: T, arg2: U): T & U => {
  let res = {} as T & U
  res = Object.assign(arg1, arg2)
  return res
}
mergeFunc({ a: 'a' }, { b: 'b' })

联合类型 ​

使用 | 表示可以是多种类型之一:

ts
type1 | type2 | type3
type1 | type2 | type3

类型保护 ​

类型谓词 ​

ts
function isString(value: string | number): value is string {
  return typeof value === 'string'
}
if (isString(item)) {
  console.log(item.length)   // 此代码块 item 为 string
}
function isString(value: string | number): value is string {
  return typeof value === 'string'
}
if (isString(item)) {
  console.log(item.length)   // 此代码块 item 为 string
}

typeof 类型保护 ​

ts
if (typeof item === 'string') {
  console.log(item.length)
}
if (typeof item === 'string') {
  console.log(item.length)
}

注意:只能使用 ===/!==,不能使用 includes。只能判断 string/number/boolean/symbol。

instanceof 类型保护 ​

用于检测类的实例。

可以为 null 的类型 ​

开启 --strictNullChecks 后,null 和 undefined 不能赋值给其他类型:

ts
let s = 'foo'
s = null           // 报错
let sn: string | null = 'foo'
sn = null          // 正常
sn = undefined     // 报错,没有 undefined
let s = 'foo'
s = null           // 报错
let sn: string | null = 'foo'
sn = null          // 正常
sn = undefined     // 报错,没有 undefined

类型别名 ​

ts
type TypeString = string
let str: TypeString = 'str'

// 对象别名可在属性中引用自身
type Childs<T> = {
  current: T
  child?: Childs<T>
}
type TypeString = string
let str: TypeString = 'str'

// 对象别名可在属性中引用自身
type Childs<T> = {
  current: T
  child?: Childs<T>
}

注意:类型别名不能使用 extends 和 implements。

字面量类型 ​

ts
type Name = 'wing'
let str: Name = 'wing'   // 只能是 'wing'

type Direction = 'north' | 'east' | 'south' | 'west'
function getDirectionFirstLetter(direction: Direction) {
  return direction.substr(0, 1)
}
type Name = 'wing'
let str: Name = 'wing'   // 只能是 'wing'

type Direction = 'north' | 'east' | 'south' | 'west'
function getDirectionFirstLetter(direction: Direction) {
  return direction.substr(0, 1)
}

可辨识联合 ​

三要素:单例类型属性、联合类型、类型保护:

ts
interface Square { kind: 'square'; size: number }
interface Rectangle { kind: 'rectangle'; height: number; width: number }
interface Circle { kind: 'circle'; radius: number }

type Shape = Square | Rectangle | Circle

function getArea(s: Shape): number {
  switch (s.kind) {
    case 'square': return s.size * s.size
    case 'rectangle': return s.height * s.width
    case 'circle': return Math.PI * s.radius ** 2
    default: return assertNever(s)  // 遗漏分支会报错
  }
}
function assertNever(value: never): never {
  throw new Error('Unexpected object: ' + value)
}
interface Square { kind: 'square'; size: number }
interface Rectangle { kind: 'rectangle'; height: number; width: number }
interface Circle { kind: 'circle'; radius: number }

type Shape = Square | Rectangle | Circle

function getArea(s: Shape): number {
  switch (s.kind) {
    case 'square': return s.size * s.size
    case 'rectangle': return s.height * s.width
    case 'circle': return Math.PI * s.radius ** 2
    default: return assertNever(s)  // 遗漏分支会报错
  }
}
function assertNever(value: never): never {
  throw new Error('Unexpected object: ' + value)
}

多态的 this 类型 ​

ts
class Counter {
  constructor(public count: number = 0) {}
  public add(value: number) {
    this.count += value
    return this
  }
}
class PowCounter extends Counter {
  public pow(value: number) {
    this.count = this.count ** value
    return this
  }
}
new PowCounter(5).add(5).pow(3)
class Counter {
  constructor(public count: number = 0) {}
  public add(value: number) {
    this.count += value
    return this
  }
}
class PowCounter extends Counter {
  public pow(value: number) {
    this.count = this.count ** value
    return this
  }
}
new PowCounter(5).add(5).pow(3)

索引类型与索引访问 ​

ts
// keyof — 获取所有属性名组成的联合类型
interface Info { name: string; age: number }
let info: keyof Info  // 'name' | 'age'

// 索引访问 — 获取属性类型
type NameT = Info['name']  // string
type Values = Info[keyof Info]  // string | number(去除 never/null/undefined)
// keyof — 获取所有属性名组成的联合类型
interface Info { name: string; age: number }
let info: keyof Info  // 'name' | 'age'

// 索引访问 — 获取属性类型
type NameT = Info['name']  // string
type Values = Info[keyof Info]  // string | number(去除 never/null/undefined)

映射类型 ​

ts
// 遍历属性,添加修饰符
type Readonly<T> = { readonly [P in keyof T]: T[P] }
type Partial<T> = { [P in keyof T]?: T[P] }
type Pick<T, K extends keyof T> = { [P in K]: T[P] }
type Record<K extends keyof any, T> = { [P in K]: T }

// 增加或移除修饰符
type ReadonlyInfo<T> = { +readonly [P in keyof T]-?: T[P] }

// 映射类型推断(包装与拆包)
type Proxy<T> = { get(): T; set(val: T): void }
type Proxify<T> = { [P in keyof T]: Proxy<T[P]> }
function unproxify<T>(t: Proxify<T>): T {
  const result = {} as T
  for (const k in t) result[k] = t[k].get()
  return result
}
// 遍历属性,添加修饰符
type Readonly<T> = { readonly [P in keyof T]: T[P] }
type Partial<T> = { [P in keyof T]?: T[P] }
type Pick<T, K extends keyof T> = { [P in K]: T[P] }
type Record<K extends keyof any, T> = { [P in K]: T }

// 增加或移除修饰符
type ReadonlyInfo<T> = { +readonly [P in keyof T]-?: T[P] }

// 映射类型推断(包装与拆包)
type Proxy<T> = { get(): T; set(val: T): void }
type Proxify<T> = { [P in keyof T]: Proxy<T[P]> }
function unproxify<T>(t: Proxify<T>): T {
  const result = {} as T
  for (const k in t) result[k] = t[k].get()
  return result
}

条件类型 ​

ts
type Type1<T> = T extends string ? string : number

// 分布式条件类型
type TypeName<T> = T extends string ? string
  : T extends number ? number
  : T extends boolean ? boolean
  : T extends undefined ? undefined
  : T extends () => void ? () => void
  : object

type Type3 = TypeName<(() => void) | string[]>  // 返回联合类型

// Diff:从联合类型中排除
type Diff<T, U> = T extends U ? never : T
type Type2 = Diff<string | number | boolean, undefined | number>  // string | boolean
type Type1<T> = T extends string ? string : number

// 分布式条件类型
type TypeName<T> = T extends string ? string
  : T extends number ? number
  : T extends boolean ? boolean
  : T extends undefined ? undefined
  : T extends () => void ? () => void
  : object

type Type3 = TypeName<(() => void) | string[]>  // 返回联合类型

// Diff:从联合类型中排除
type Diff<T, U> = T extends U ? never : T
type Type2 = Diff<string | number | boolean, undefined | number>  // string | boolean

infer 类型推断 ​

ts
// 获取函数参数类型
type Type11<T> = T extends Array<infer U> ? U : T
type Type9 = Type11<string[]>  // string

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any
// 获取函数参数类型
type Type11<T> = T extends Array<infer U> ? U : T
type Type9 = Type11<string[]>  // string

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

预定义条件类型 ​

类型说明
Exclude<T, U>从 T 剔除可赋值给 U 的类型
Extract<T, U>提取 T 中可赋值给 U 的类型
NonNullable<T>从 T 剔除 null 和 undefined
ReturnType<T>获取函数返回值类型
InstanceType<T>获取构造函数类型的实例类型
ts
type T1 = Exclude<'a' | 'b' | 'c', 'b' | 'c'>   // 'a'
type T2 = ReturnType<() => string>               // string
type T3 = InstanceType<typeof C>                 // C
type T1 = Exclude<'a' | 'b' | 'c', 'b' | 'c'>   // 'a'
type T2 = ReturnType<() => string>               // string
type T3 = InstanceType<typeof C>                 // C

TypeScript 关键词 ​

typeof ​

获取变量或对象的类型:

ts
const sem: Person = { name: 'hhh', age: 20 }
type Sem = typeof sem                     // Person

function toArray(x: number): Array<number> { return [x] }
type Func = typeof toArray                // (x: number) => Array<number>

const kakuqo = { name: 'kakuqo', address: { province: '北京' } }
type Kakuqo = typeof kakuqo              // { name: string; address: { province: string } }
const sem: Person = { name: 'hhh', age: 20 }
type Sem = typeof sem                     // Person

function toArray(x: number): Array<number> { return [x] }
type Func = typeof toArray                // (x: number) => Array<number>

const kakuqo = { name: 'kakuqo', address: { province: '北京' } }
type Kakuqo = typeof kakuqo              // { name: string; address: { province: string } }

const 断言 ​

ts
let x = 'hello' as const       // type X = 'hello'
let y = [10, 20] as const      // readonly [10, 20]
let z = { text: 'hello' } as const  // { readonly text: 'hello' }

// 获取元组元素值的联合类型
type Data = (typeof y)[number]
let x = 'hello' as const       // type X = 'hello'
let y = [10, 20] as const      // readonly [10, 20]
let z = { text: 'hello' } as const  // { readonly text: 'hello' }

// 获取元组元素值的联合类型
type Data = (typeof y)[number]

keyof ​

获取对象所有 key 的联合类型:

ts
interface Person { name: string; age: number }
type K1 = keyof Person           // 'name' | 'age'
type K2 = keyof Person[]         // "length" | "toString" | "pop" | ...

// keyof + typeof
const COLORS = { red: 'red', blue: 'blue' }
type C = keyof typeof COLORS     // 'red' | 'blue'
interface Person { name: string; age: number }
type K1 = keyof Person           // 'name' | 'age'
type K2 = keyof Person[]         // "length" | "toString" | "pop" | ...

// keyof + typeof
const COLORS = { red: 'red', blue: 'blue' }
type C = keyof typeof COLORS     // 'red' | 'blue'

in ​

遍历枚举类型:

ts
type Keys = 'a' | 'b' | 'c'
type Obj = { [p in Keys]: any }  // { a: any, b: any, c: any }
type Keys = 'a' | 'b' | 'c'
type Obj = { [p in Keys]: any }  // { a: any, b: any, c: any }

infer ​

在条件类型中声明类型变量:

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

extends ​

泛型约束:

ts
interface ILengthwise { length: number }
function loggingIdentity<T extends ILengthwise>(arg: T): T {
  console.log(arg.length)
  return arg
}
interface ILengthwise { length: number }
function loggingIdentity<T extends ILengthwise>(arg: T): T {
  console.log(arg.length)
  return arg
}

类型收集 ​

React.forwardRef 类型 ​

ts
interface RsRefForwardingComponent<T extends React.ElementType, P = unknown> {
  <As extends React.ElementType = T>(
    props: React.PropsWithChildren<ReplaceProps<As, WithAsProps<As> & P>>,
    context?: any,
  ): React.ReactElement | null
  propTypes?: any
  displayName?: string
}
const Button: RsRefForwardingComponent<'button', ButtonProps> = React.forwardRef(...)
interface RsRefForwardingComponent<T extends React.ElementType, P = unknown> {
  <As extends React.ElementType = T>(
    props: React.PropsWithChildren<ReplaceProps<As, WithAsProps<As> & P>>,
    context?: any,
  ): React.ReactElement | null
  propTypes?: any
  displayName?: string
}
const Button: RsRefForwardingComponent<'button', ButtonProps> = React.forwardRef(...)

SafeOmit ​

解决 Omit 丢失索引签名问题:

ts
type Convert<T> = { [K in keyof T]: string extends K ? never : number extends K ? never : symbol extends K ? never : K }
type Keys<T extends { [x: string]: any; [x: number]: any }> = Convert<T> extends { [_ in keyof T]: infer U } ? U : never
type SafeOmit<T, K extends string | number | symbol> = Exclude<Keys<T>, K> extends keyof T ? Pick<T, Exclude<Keys<T>, K>> : {}
type Convert<T> = { [K in keyof T]: string extends K ? never : number extends K ? never : symbol extends K ? never : K }
type Keys<T extends { [x: string]: any; [x: number]: any }> = Convert<T> extends { [_ in keyof T]: infer U } ? U : never
type SafeOmit<T, K extends string | number | symbol> = Exclude<Keys<T>, K> extends keyof T ? Pick<T, Exclude<Keys<T>, K>> : {}