Typescript技巧,优势和缺陷
发表于更新于阅读时长 3 分钟收集Typescript
相关
技巧
- Omit (2.8+)
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
- Overwrite
type Overwrite<A extends object, B extends object> = Pick<A, Exclude<keyof A, keyof B>> & B
- Diff
type Diff<A extends object, K extends keyof A> = Omit<A, K> & Partial<Pick<A, K>>
- Equal
type Equal<U, V> = U extends V ? (V extends U ? true : false) : false
- DeepReadOnly
type DeepReadonly<A> = {
readonly [K in keyof A]: A[K] extends Object ? DeepReadonly<A[K]> : A[K]
}
- diff union
type Diff<T extends string, U extends string> = ({ [P in T]: P } &
{ [P in U]: never } & { [x: string]: never })[T]
- dePromise(2.8+,其他用法类似)
type DeboxPromise<T> = T extends Promise<infer U> ? U : never
- filter is
const arr: (number | string)[] = []
arr.filter((item): item is number => typeof item === 'number') // get number[]
- 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]
- tuple to union(3.0+)
type T2U<T extends any[]> = T[number]
- tuple to intersection(3.0+)
type U2I<U> = ((k: U) => void) extends (k: infer I) => void ? I : never
- const context(3.4+)
const str = 'str' // string
const lit = 'lit' as const // 'lit'
优势
- Intersection Types
- 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
}
}
- Literal Types
- Polymorphic this Types
可以指定函数能在什么样的 context 里被调用 - Index Types
在 Typescript 2.8+ 里只要&Record<T, U>
即可 - Mapped Types
- 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
- 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
缺陷
-
协变和浅拷贝
const foo: number[] = [1, 2, 3] const bar: (number | string)[] = foo bar[0] = '1' foo[0].toFixed() // error!
解决方法:
- immutable(效率低)
- type-safe runtime(不存在)
- 别
-
协变和范型
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(效率低)
-
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 修
-
不支持 HKT