深入 TypeScript 的高级特性,掌握类型体操、条件类型、映射类型等进阶技巧,让你的类型系统更加强大。
type Person = {
name: string
age: number
}
type Employee = {
employeeId: string
department: string
}
type EmployeePerson = Person & Employee
const employee: EmployeePerson = {
name: '东巴文',
age: 18,
employeeId: 'E001',
department: '技术部'
}
// 合并函数类型
type LogFunc = (msg: string) => void
type ErrorFunc = (err: Error) => void
type Logger = LogFunc & ErrorFunc
const logger: Logger = (input: string | Error) => {
console.log(input)
}
type ID = string | number
type Status = 'pending' | 'active' | 'inactive'
// 可辨识联合
interface Circle {
kind: 'circle'
radius: number
}
interface Rectangle {
kind: 'rectangle'
width: number
height: number
}
interface Triangle {
kind: 'triangle'
base: number
height: number
}
type Shape = Circle | Rectangle | Triangle
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2
case 'rectangle':
return shape.width * shape.height
case 'triangle':
return (shape.base * shape.height) / 2
}
}
type User = {
id: number
name: string
email: string
age: number
}
// 索引访问类型
type UserId = User['id'] // number
type UserName = User['name'] // string
type UserKeys = keyof User // 'id' | 'name' | 'email' | 'age'
// 数组元素类型
type StringArrayElement = string[][number] // string
// 嵌套索引
type Config = {
database: {
host: string
port: number
}
}
type DbHost = Config['database']['host'] // string
type IsString<T> = T extends string ? true : false
type A = IsString<string> // true
type B = IsString<number> // false
// 嵌套条件类型
type TypeName<T> =
T extends string ? 'string' :
T extends number ? 'number' :
T extends boolean ? 'boolean' :
T extends undefined ? 'undefined' :
T extends Function ? 'function' :
'object'
type T1 = TypeName<string> // 'string'
type T2 = TypeName<number[]> // 'object'
type MessageOf<T> = T extends { message: unknown } ? T['message'] : never
type EmailMessage = { message: string }
type ErrorMessage = { message: Error }
type Email = MessageOf<EmailMessage> // string
type Error1 = MessageOf<ErrorMessage> // Error
type Empty = MessageOf<{}> // never
// 约束条件类型
type Flatten<T> = T extends Array<infer U> ? U : T
type Str = Flatten<string[]> // string
type Num = Flatten<number> // number
// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type R1 = ReturnType<() => string> // string
type R2 = ReturnType<(x: number) => boolean> // boolean
// 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never
type P1 = Parameters<(a: string, b: number) => void> // [string, number]
// 提取 Promise 值类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T
type Value = UnwrapPromise<Promise<string>> // string
// 提取数组元素类型
type ElementOf<T> = T extends Array<infer E> ? E : never
type Elem = ElementOf<string[]> // string
// 提取构造函数参数
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never
class User {
constructor(public name: string, public age: number) {}
}
type UserParams = ConstructorParameters<typeof User> // [string, number]
// 提取实例类型
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : never
type UserInstance = InstanceType<typeof User> // User
// 联合类型会分布式应用
type ToArray<T> = T extends any ? T[] : never
type StrArr = ToArray<string | number> // string[] | number[]
// 避免分布式行为
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type Arr = ToArrayNonDist<string | number> // (string | number)[]
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
type Partial<T> = {
[P in keyof T]?: T[P]
}
type Required<T> = {
[P in keyof T]-?: T[P]
}
type Mutable<T> = {
-readonly [P in keyof T]: T[P]
}
// 使用
interface User {
id: number
name: string
age?: number
}
type ReadonlyUser = Readonly<User>
type PartialUser = Partial<User>
type RequiredUser = Required<User>
interface User {
readonly id: number
name: string
age?: number
}
// 移除 readonly
type MutableUser = {
-readonly [K in keyof User]: User[K]
}
// 移除可选
type RequiredUser = {
[K in keyof User]-?: User[K]
}
// 添加可选
type OptionalUser = {
[K in keyof User]+?: User[K]
}
// 使用 as 重命名键
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
interface Person {
name: string
age: number
}
type PersonGetters = Getters<Person>
// {
// getName: () => string
// getAge: () => number
// }
// 过滤键
type OnlyStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K]
}
type StringProps = OnlyStrings<{ name: string; age: number; email: string }>
// { name: string; email: string }
type EventName = 'click' | 'focus' | 'blur'
type EventHandler = `on${Capitalize<EventName>}`
// 'onClick' | 'onFocus' | 'onBlur'
type HTTPMethod = 'get' | 'post' | 'put' | 'delete'
type APIEndpoint = `/api/${string}`
type Route = `${HTTPMethod} ${APIEndpoint}`
// 'get /api/...' | 'post /api/...' | ...
// 内置工具类型
type LowercaseName = Lowercase<'HELLO'> // 'hello'
type UppercaseName = Uppercase<'hello'> // 'HELLO'
type CapitalizeName = Capitalize<'hello'> // 'Hello'
type UncapitalizeName = Uncapitalize<'Hello'> // 'hello'
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}
interface Config {
server: {
host: string
port: number
}
database: {
host: string
port: number
credentials: {
username: string
password: string
}
}
}
type PartialConfig = DeepPartial<Config>
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
}
// 按值类型挑选
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K]
}
interface User {
name: string
age: number
email: string
active: boolean
}
type StringProps = PickByType<User, string>
// { name: string; email: string }
type NumberProps = PickByType<User, number>
// { age: number }
// 按值类型排除
type OmitByType<T, U> = {
[K in keyof T as T[K] extends U ? never : K]: T[K]
}
type Path<T, K extends keyof T = keyof T> =
K extends string | number
? T[K] extends object
? K | `${K}.${Path<T[K]>}`
: K
: never
interface Config {
server: {
host: string
port: number
}
db: {
url: string
options: {
ssl: boolean
}
}
}
type ConfigPath = Path<Config>
// 'server' | 'server.host' | 'server.port' | 'db' | 'db.url' | 'db.options' | 'db.options.ssl'
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type Union = { a: string } | { b: number }
type Intersection = UnionToIntersection<Union>
// { a: string } & { b: number }
type UnionToTuple<T, Last = LastOfUnion<T>> =
[T] extends [never]
? []
: [...UnionToTuple<Exclude<T, Last>>, Last]
type LastOfUnion<T> =
UnionToIntersection<T extends any ? () => T : never> extends () => infer R
? R
: never
type Tuple = UnionToTuple<'a' | 'b' | 'c'>
// ['a', 'b', 'c']
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
// { title: string; completed: boolean }
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
type TodoWithoutDescription = MyOmit<Todo, 'description'>
// { title: string; completed: boolean }
type MyReadonly<T> = {
readonly [P in keyof T]: T[P]
}
type MyExclude<T, U> = T extends U ? never : T
type Result = MyExclude<'a' | 'b' | 'c', 'a'>
// 'b' | 'c'
type MyExtract<T, U> = T extends U ? T : never
type Result = MyExtract<'a' | 'b' | 'c', 'a' | 'b'>
// 'a' | 'b'
type MyNonNullable<T> = T extends null | undefined ? never : T
type Result = MyNonNullable<string | null | undefined>
// string
type MyReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : never
function fn() {
return { x: 10, y: 20 }
}
type R = MyReturnType<typeof fn>
// { x: number; y: number }
// 数组扁平化类型
type Flatten<T> = T extends Array<infer U>
? U extends Array<any>
? Flatten<U>
: U
: T
type Flat = Flatten<[1, [2, [3, [4]]]]>
// number
// 深度属性访问
type DeepValue<T, P extends string> =
P extends `${infer K}.${infer Rest}`
? K extends keyof T
? DeepValue<T[K], Rest>
: never
: P extends keyof T
? T[P]
: never
interface Config {
server: {
host: string
port: number
}
}
type Host = DeepValue<Config, 'server.host'> // string
type Port = DeepValue<Config, 'server.port'> // number
// 对象键值互换
type SwapKeyValue<T extends Record<string, string>> = {
[K in keyof T as T[K]]: K
}
const colors = {
red: '#ff0000',
green: '#00ff00',
blue: '#0000ff'
} as const
type ColorNames = SwapKeyValue<typeof colors>
// { '#ff0000': 'red'; '#00ff00': 'green'; '#0000ff': 'blue' }
// 提取对象方法
type Methods<T> = {
[K in keyof T as T[K] extends Function ? K : never]: T[K]
}
class UserService {
name = '东巴文'
getName() { return this.name }
setName(name: string) { this.name = name }
}
type UserServiceMethods = Methods<UserService>
// { getName: () => string; setName: (name: string) => void }
// 过于复杂
type Complex<T> = T extends object
? { [K in keyof T]: T[K] extends Function ? never : Complex<T[K]> }[keyof T]
: T
// 简化
type Simplify<T> = T extends object
? { [K in keyof T]: Simplify<T[K]> }
: T
// 自定义工具类型库
type Utils = {
DeepPartial: <T>(obj: T) => DeepPartial<T>
DeepReadonly: <T>(obj: T) => DeepReadonly<T>
RequiredKeys: <T>(obj: T) => { [K in keyof T]-?: T[K] }
OptionalKeys: <T>(obj: T) => { [K in keyof T]+?: T[K] }
}
/**
* 深度可选类型
* 递归地将所有属性变为可选
* @template T - 要转换的类型
* @example
* type Config = DeepPartial<{ server: { host: string } }>
* // { server?: { host?: string } }
*/
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}
掌握 TypeScript 进阶后,你可以继续学习:
东巴文(db-w.cn)—— 让 TypeScript 更强大