Typescript技巧,优势和缺陷

发表于更新于阅读时长 3 分钟

收集Typescript相关

技巧

  1. Omit (2.8+)
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
  1. Overwrite
type Overwrite<A extends object, B extends object> = Pick<A, Exclude<keyof A, keyof B>> & B
  1. Diff
type Diff<A extends object, K extends keyof A> = Omit<A, K> & Partial<Pick<A, K>>
  1. Equal
type Equal<U, V> = U extends V ? (V extends U ? true : false) : false
  1. DeepReadOnly
type DeepReadonly<A> = {
  readonly [K in keyof A]: A[K] extends Object ? DeepReadonly<A[K]> : A[K]
}
  1. diff union
type Diff<T extends string, U extends string> = ({ [P in T]: P } &
  { [P in U]: never } & { [x: string]: never })[T]
  1. dePromise(2.8+,其他用法类似)
type DeboxPromise<T> = T extends Promise<infer U> ? U : never
  1. filter is
const arr: (number | string)[] = []
arr.filter((item): item is number => typeof item === 'number') // get number[]
  1. shift tuple(3.0+)
type Shift<Tuple extends any[]> = ((...x: Tuple) => void)
    extends ((h: any, ...rest: infer R) => void)
    ? R
    : never

type x = Shift<[1, 2, 3, 4]> // [2, 3, 4]
  1. tuple to union(3.0+)
type T2U<T extends any[]> = T[number]
  1. tuple to intersection(3.0+)
type U2I<U> = ((k: U) => void) extends (k: infer I) => void ? I : never
  1. const context(3.4+)
const str = 'str' // string
const lit = 'lit' as const // 'lit'

优势

  1. Intersection Types
  2. Discriminated Unions
interface Circle {
  type: 'circle'
  radius: number
}
interface Rectangle {
  type: 'rectangle'
  width: number
  length: number
}
interface Triangle {
  type: 'triangle'
  a: number
  b: number
  c: number
}
function perimeter(shape: Circle | Rectangle | Triangle): number {
  // error! Maybe undefined
  switch (shape.type) {
    case 'circle':
      return Math.PI * shape.radius * 2
    case 'rectangle':
      return (shape.width + shape.length) * 2
    // case 'triangle': return shape.a + shape.b + shape.c
  }
}
  1. Literal Types
  2. Polymorphic this Types
    可以指定函数能在什么样的 context 里被调用
  3. Index Types
    在 Typescript 2.8+ 里只要&Record<T, U>即可
  4. Mapped Types
  5. Strict Function Check(2.6+)
    开启此 flag 之后, 函数参数是逆变(更宽松)的, 返回值是协变(更详细)的
const g = new GrayHound()
function f(g: (i: Dog) => Dog) {}
const g1 = (i: Animal): Animal => {}
const g2 = (i: Animal): GrayHound => {}
const g3 = (i: GrayHound): Animal => {}
const g4 = (i: GrayHound): GrayHound => {}
f(g1) // error, because in f, g1 will always return an Animal, which is not always Dog
f(g2) // success
f(g3) // error, because in f, g3 will always be called with a Dog, which is not always GrayHound
f(g4) // error
  1. Optional and Rest in Tuple(3.0+)
type Foo = [number, string?, boolean?]
const foo: Foo = [1]
type Bar = [number, number, ...number[]]
const bar: Bar = [1] // error, must be at least 2

缺陷

  1. 协变和浅拷贝

    const foo: number[] = [1, 2, 3]
    const bar: (number | string)[] = foo
    bar[0] = '1'
    foo[0].toFixed() // error!

    解决方法:

    • immutable(效率低)
    • type-safe runtime(不存在)
  2. 协变和范型

    class Box<V> {
      item: V
    }
    function f<T extends Box<Animal>>(box: T) {
      box.item = new Cat()
      return box // return type is wrongly infered as Box<Animal>
    }
    const result = f(new Box<Dog>())
    result.item.bark() // error!

    解决方法:

    • 手动标记返回值类型(推荐)
    • immutable(效率低)
  3. getter 和 setter 的类型必须相同

    class MyClass {
      private _myDate: Date
    
      get myDate(): Date {
        // remove return type will cause myDate be infered as Date | number
        return this._myDate
      }
    
      set myDate(value: Date | number) {
        if (typeof value === 'number') {
          this._myDate = new Date(value)
        } else {
          this._myDate = value
        }
      }
    }

    解决方法:

    • 手动 cast
    • 别用默认的 getter/setter
    • 等 devteam 修
  4. 不支持 HKT

© 2016 - 2023Austaras Devas