类型计算
Keyof 操作符
type Point = { x: number; y: number }
type P = keyof Point // "x" | "y"
type Arrayish = { [n: number]: unknown }
type A = keyof Arrayish // number
type Mapish = { [k: string]: boolean }
type M = keyof Mapish // string | number
typescript
M
类型是 string | number
,因为对于对象来说,虽然规定 k 是 string,但是也可以使用数字作为键。
typeof
console.log(typeof 'xxx') // string
let s = 'hello'
let n: typeof s // string
typescript
typeof
在 javascript 是计算操作符,不过在 ts 中,属于类型操作符(不是运行时执行,coding 时也会检查)。
Partial Type
部分类型,ts 的延伸能力。
interface Todo {
title: string
description: string
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return {
...todo,
...fieldsToUpdate
}
}
const todo1 = {
title: 'organize desk',
description: 'clear clutter'
}
const todo2 = updateTodo(todo1, {
description: 'throw out trash'
})
typescript
如何实现?
type Partial<T> = {
[P in keyof T]?: T[P]
}
typescript
Required
interface Props {
a?: number
b?: number
}
const obj: Props = { a: 5 }
const obj2: Required<Props> = { a: 5 }
// 类型 "{ a: number; }" 中缺少属性 "b",但类型 "Required<Props>" 中需要该属性。
typescript
如何实现?
type Required<T> = {
[P in keyof T]-?: T[P]
}
typescript
Readonly
interface Todo {
title: string
}
const todo: Readonly<Todo> = {
title: 'Delete inactive users'
}
todo.title = 'Hello'
// 无法为“title”赋值,因为它是只读属性。
typescript
如何实现?
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
typescript
Record
记录。描述 key value 值的类型。
interface CatInfo {
age: number
breed: string
}
type CateName = 'miffy' | 'boris' | 'mordred'
const cats: Record<CateName, CatInfo> = {
miffy: { age: 10, breed: 'persian' },
boris: { age: 5, breed: 'Maine Coon' },
mordred: { age: 10, breed: 'Britsh Shorthair' }
}
typescript
如何实现?
type Record<K extends keyof any, T> = {
[P in K]: T
}
typescript
日常使用:
const obj = Record<string, object>
typescript
Pick
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = Pick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false
}
typescript
如何实现?
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
typescript
Exclude
type T0 = Exclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
type T1 = Exclude<'a' | 'b' | 'c', 'a' | 'b'> // 'c'
type T2 = Exclude<string | number | (() => void), Function> // string | number
typescript
如何实现?
type Exclude<T, U> = T extends U ? never : T
typescript
Omit
省略。与 Pick 是相反的操作。
Exclude 操作的是联合类型,Omit 操作的是接口。
interface Todo {
title: string
description: string
completed: boolean
createdAt: number
}
type TodoPreview = Omit<Todo, 'description'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
createdAt: 1681344482208
}
type TodoInfo = Omit<Todo, 'completed' | 'createdAt'>
const todoInfo: TodoInfo = {
title: 'Pick up kids',
description: 'Kindergarten closes at 5pm'
}
typescript
如何实现:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
typescript
Extract
type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'> // 'a'
type T1 = Extract<string | number | (() => void), Function> // () => void
typescript
如何实现?
type Extract<T, U> = T extends U ? T : never
typescript
可以看到,Extract 和 Exclude 是一对组合;Pick 和 Omit 是一对组合。
NonNullable
type T0 = NonNullable<string | number | undefined> // string | number
type T1 = NonNullable<string[] | null | undefined> // string[]
typescript
如何实现:
type NonNullable<T> = T extends null | undefined ? never : T
typescript
Parmameters
declare function f1(args: { a: number; b: string }) : void
type T0 = Parameters<() => string> // []
type T1 = Parameters<(s: string) => void> // [s: string]
type T2 = Parameters<<T>(args: T) => T> // [args: unknown]
type T3 = Parameters<typeof f1> // [args: { a:number; b: string }]
typescript
如何实现:
type Parameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never
typescript
ConstructorParameters
type T0 = ConstructorParameters<ErrorConstructor> // [message?: string | undefiend]
type T1 = ConstructorParameters<FunctionConstructor> // string[]
type T2 = ConstructorParameters<RegExpConstructor> // [pattern: string | RegExp, flags?: string | undefined]
type T3 = ConstructorParameters<any> // unkown[]
type T4 = ConstructorParameters<Function>
// 类型“Function”不满足约束“abstract new (...args: any) => any”。
// 类型“Function”提供的内容与签名“new (...args: any): any”不匹配。
typescript
如何实现:
type ConstructorParameters<T extends abstract new (...args: any) => any> =
T extends abstract new (...args: infer P) => any ? P : never
typescript
ReturnType
declare function f1(): { a: number; b: string }
type T0 = ReturnType<() => string> // string
type T1 = ReturnType<(s: string) => void> // void
type T2 = ReturnType<<T>() => T> // unkown
type T3 = ReturnType<<T extends U, U extends number[]>() => T> // number[]
type T4 = ReturnType<typeof f1> // { a: number; b: string }
type T5 = ReturnType<any> // any
type T6 = ReturnType<never> // never
type T7 = ReturnType<string> // 类型“string”不满足约束“(...args: any) => any”。
type T8 = ReturnType<Function> // 类型“Function”不满足约束“(...args: any) => any”。
typescript
如何实现:
type ReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : any
typescript
InstanceType
class C {
x = 0
y = 0
}
type T0 = InstanceType<typeof C> // C
type T1 = InstanceType<any> // any
type T2 = InstanceType<never> // never
type T3 = InstanceType<string> // 类型“string”不满足约束“abstract new (...args: any) => any”。
type T4 = InstanceType<Function> // 类型“Function”不满足约束“abstract new (...args: any) => any”。
typescript
如何实现?
type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (
...args: any
) => infer R
? R
: any
typescript
ThisParameterType
function toHex(this: Number) {
return this.toString(16)
}
function numberToString(n: ThisParameterType<typeof toHex>) {
return toHex.apply(n)
}
typescript
如何实现?
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown
typescript
OmitThisParameter
function toHex(this: Number) {
return this.toString(16)
}
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5)
console.log(fiveToHex())
typescript
如何实现?
type OmitThisParameter<T> = unknown extends ThisParameterType<T>
? T
: T extends (...args: infer A) => infer R
? (...args: A) => R
: T
typescript
ThisType
提供 this 提示。
type ObjectDescriptor<D, M> = {
data?: D
methods: M & ThisType<D & M>
}
function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
let data: object = desc.data || {}
let methods: object = desc.methods || {}
return {
...data,
...methods
} as D & M
}
let obj = makeObject({
data: { x: 0, y: 0 },
methods: {
moveBy(dx: number, dy: number) {
this.x += dx
this.y += dy
}
}
})
obj.x = 10
obj.y = 20
obj.moveBy(5, 5)
typescript
// tsconfig.json
{
"compilerOptions": {
"target": "es6",
"lib": ["es6"],
"esModuleInterop": true,
"noImplicitThis": true, // 需要打开这个配置
"downlevelIteration": true,
"module": "CommonJS"
}
}
json
如何实现?
interface ThisType<T> {}
typescript
Uppercase/LowerCase
type Greeting = 'Hello, world'
type ShoutGreeting = Uppercase<Greeting> // HELLO, WORLD
type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<'my_app'> // ID-MY_APP
type QuietGreeting = Lowercase<ShoutGreeting> // hello, world
typescript
如何实现?
type Uppercase<S extends string> = intrinsic
type Lowercase<S extends string> = intrinsic
type Capitalize<S extends string> = intrinsic
type Uncapitalize<S extends string> = intrinsic
typescript
intrinsic 代表这个实现是内部实现,不是 ts 直接可以使用。
总结
类型是可以计算的吗?答案当然是肯定的。
- &
-
- ?
- infer
- …