TypeScript 高级类型

408 阅读4分钟

Typescript 的高级类型 就是生成类型的类型。

内置高级类型

//file: node_modules/typescript/lib/lib.es5.d.ts
type Partial<T> = {
    [P in keyof T]?: T[P] 
}

type Required<T> = {
    [P in keyof T]-?: T[P] 
}

type Readonly<T> = {
    readonly [P in keyof T]: T[P]
}

type Pick<T, K extends keyof T> = {
    [P in K]: T[P]
}

type KeyOfAny = keyof any // string | number | symbol
type Reacord<K extends keyof any, T> = {
    [P in K]: T
}

type Exclude<T, K> = T extends K ? never : T

type Extract<T, K> = T extends K ? T : never

// type Omit<T, K extends keyof T> = {
//     [P in Exclude<keyof T, K>]: T[P]
// }
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

type NonNullable<T> = T extends null | undefined ? never : T

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never

type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never

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

type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any

type Uppercase<S extends string> = intrinsic

type Lowercase<S extends string> = intrinsic

type Capitalize<S extends string> = intrinsic

type Uncapitalize<S extends string> = intrinsic

interface ThisType<T> { }

// this 详解: https://juejin.cn/post/6927507521783627790
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown

type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends (...args: infer A) => infer R ? (...args: A) => R : T

集合操作

// 并集
type ToUnion<A, B> = A | B
type ToUnionDemo = ToUnion<'a'|'b'|'d', 'a'|'b'|'c'>

// 交集
type ToIntersection<A, B> = A extends B ? A : never
// type ToIntersectionDemo = ToIntersection<'a'|'b'|'d', 'a'|'b'|'c'>
// or
type ToIntersectionDemo = Extract<'a'|'b'|'d', 'a'|'b'|'c'>

// 差集
type Difference<A, B> = A extends B ? never : A
type ToDifference<A, B> = Difference<A | B, A & B>
// type ToDifferenceDemo = ToDifference<'a'|'b'|'d', 'a'|'b'|'c'>
// or
type ToDifferenceDemo = Exclude<'a'|'b'|'d', 'a'|'b'|'c'>

infer

// 核心:声明变量

// 获取 Promise 类型值的类型
type PromiseValue<T> = T extends Promise<infer U> ? U : T
type PromiseValueDemo = PromiseValue<Promise<{a: string}>>

// shift
type ShiftOfArr<T extends Array<any>> = T extends [any, ...infer U] ? U : []
type ShiftOfArrDemo = ShiftOfArr<[string, number, undefined]>

// unShift
type UnShiftOfArr<T extends Array<any>, V> = [V, ...T]
type UnShiftOfArrDemo = UnShiftOfArr<[string, number, undefined]>

// push
type PushOfArr<T extends Array<any>, V> = T extends [...infer U] ? [...U, V] : []
type PushOfArrDemo = PushOfArr<[string, number, undefined], null>

// pop
type PopOfArr<T extends Array<any>> = T extends [...infer U, infer L] ? U : []
type PopOfArrDemo = PopOfArr<[string, number, undefined]>

// trimLeft
type TrimLeft<S extends string> = S extends `${' ' | '\t' | '\n'}${infer R}` ? TrimLeft<R> : S
type TrimLeftDemo = TrimLeft<'  7yue  '>

// trimRight
type TrimRight<S extends string> = S extends `${infer R}${' ' | '\t' | '\n'}` ? TrimRight<R> : S
type TrimRightDemo = TrimRight<'  7yue  '>

// trim
type Trim<S extends string> = TrimRight<TrimLeft<S>>
type TrimDemo = Trim<'  7yue  '>

// replace
type Replace<S extends string, F extends string, T extends string>
    = S extends `${infer L}${F}${infer R}` ? Replace<`${L}${T}${R}`, F, T> : S
type ReplaceDemo = Replace<'7yue~mmmm~', 'm', 'z'>

// 将联合类型转为对应的交叉类型 (协变, 逆变)
type UnionToInterSection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never
type UnionToInterSectionDemo = UnionToInterSection<{a: string} | {b: number} | {c: undefined}>

算数运算

// 条件判断 ==> extends
// 循环 ==> 递归

type NumberToArray<T extends number, R extends any[] = []> = R['length'] extends T ? R : NumberToArray<T, [...R, any]>

// 加法
type Add<L extends number, R extends number> = [...NumberToArray<L>, ...NumberToArray<R>]['length']
type AddDemo = Add<3, 4>

// 减法
type Sub<L extends number, R extends number>
    = NumberToArray<L> extends [...NumberToArray<R>, ...infer Result] ? Result['length'] : never
type SubDemo =Sub<3, 1>

// 乘法
type Multi<L extends number, R extends number, CArr extends any[] = [], TArr extends any[] = []>
    = CArr['length'] extends R ?  TArr['length'] : Multi<L, R, [...CArr, any], [...TArr, ...NumberToArray<L>]>
type MultiDemo = Multi<3, 4>

// 除法
type Divide<L extends number, R extends number, TArr extends any[] = []>
    = L extends 0 ? TArr['length'] : Divide<Sub<L, R>, R, [...TArr, any]>
type DivideDemo = Divide<12, 4>

简易加法表达式编译器

type ASTExpressionNode = {
  type: 'operator' | 'expression'
  left?: ASTExpressionNode
  right?: ASTExpressionNode
  value?: keyof NumberMap
}

type Parse<T> = T extends `${infer ExpressionA} + ${infer ExpressionB}`
    ? {
        type: 'operator',
        left: Parse<ExpressionA>,
        right: Parse<ExpressionB>
    }
    : {
        type: 'expression',
        value: T extends keyof NumberMap ? T : never
    }

type NumberToArray<T extends number, R extends any[] = []> = R['length'] extends T ? R : NumberToArray<T, [...R, any]>
type Add<L extends number, R extends number> = [...NumberToArray<L>, ...NumberToArray<R>]['length']

type GetValue<T extends ASTExpressionNode> = T['value'] extends string ? T['value'] : never
type GetLeft<T extends ASTExpressionNode> = T['left'] extends ASTExpressionNode ? T['left'] : never
type GetRight<T extends ASTExpressionNode> = T['right'] extends ASTExpressionNode ? T['right'] : never

// string to number
type NumberMap = {
  '0': 0,
  '1': 1,
  '2': 2,
  '3': 3,
  '4': 4,
  '5': 5,
  '6': 6,
  '7': 7,
  '8': 8,
  '9': 9,
}

type Evaluate<T extends ASTExpressionNode> = T['type'] extends 'expression'
    ? NumberMap[`${GetValue<T>}`]
    : Add<Evaluate<GetLeft<T>>, Evaluate<GetRight<T>>>

type Calculator<T extends string> = Evaluate<Parse<T>>

type ParseDemo = Parse<'1 + 2'>;
type CalculatorDemo = Calculator<'1 + 2 + 7'>

Exclude

interface IToExclude {
    a: string
    b: number | undefined
    c: undefined
    d: null
    e?: string
}

// 获取去除指定类型的字段组成的联合类型
type ExcludeTypeToUnionKey<T, K> = {
    [P in keyof T]-?: [T[P]] extends [K] ? [K] extends [T[P]] ? never : P : P
}[keyof T]

type ExcludeTypeToUnionKeyDemo = ExcludeTypeToUnionKey<IToExclude, number | undefined>


// 获取去除指定类型的联合类型
type ExcludeKeyOfTypeToUnion<T, K> = {
    [P in keyof T]-?: [T[P]] extends [K] ? [K] extends [T[P]] ? never : {[Q in P]: T[P]} : {[Q in P]: T[P]}
}[keyof T]

type ExcludeKeyOfTypeToUnionKey = ExcludeKeyOfTypeToUnion<IToExclude, number | undefined>

// 去除指定类型
type ExcludeKeyOfType<T, K> = {
    [P in ExcludeTypeToUnionKey<T, K>]: T[P]
}
// or
// type ExcludeKeyOfType<T, K> = Pick<T, ExcludeTypeToUnionKey<T, K>> // Pick 保留了可选修饰符[?]

type ExcludeKeyOfTypeDemo = ExcludeKeyOfType<IToExclude, number | string>

Partial

interface IToPartial {
    a: string
    b: number | undefined
    c?: string
    d: number
}

// type ToOptional<T> = Partial<T>
type ToOptional<T> = {
    [K in keyof T]?: T[K]
}
type ToOptionalDemo = ToOptional<IToPartial>

// 指定字段为可选
// type ToOptionalWithKey<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>
type ToOptionalWithKey<T, K extends keyof T> = {
    [P in K]?: T[P]
} & {
    [P in Exclude<keyof T, K>]: T[P]
} 
type ToOptionalWithKeyDemo = ToOptionalWithKey<IToPartial, 'a'>

// 去除可选类型后key组成的联合类型
type IsOptional<T, K extends keyof T> = Partial<Pick<T, K>> extends Pick<T, K> ? true : false
type ExcludeOptionalToUnionKey<T> = {
    [P in keyof T]-?:  IsOptional<T, P> extends true ? never : P
}[keyof T]
type ExcludeOptionalToUnionKeyDemo = ExcludeOptionalToUnionKey<IToPartial>

// 去除可选类型
type ExcludeOptional<T> = {
    [P in ExcludeOptionalToUnionKey<T>]: T[P]
}
// or
// type ExcludeOptional<T> = Pick<T, ExcludeOptionalToUnionKey<T>>
type ExcludeOptionalDemo = ExcludeOptional<IToPartial>

Required

interface IToRequired {
    a: string
    b: number | undefined
    c?: string
    d: number
    e?: symbol
}

// type ToRequired<T> = Required<T>
type ToRequired<T> = {
    [K in keyof T]-?: T[K]
}
type ToRequiredDemo = ToRequired<IToRequired>

// 指定字段为必填
// type ToRequiredWithKey<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>
type ToRequiredWithKey<T, K extends keyof T> = {
    [P in K]-?: T[P]
} & {
    [P in Exclude<keyof T, K>]: T[P]
} 
type ToRequiredWithKeyDemo = ToRequiredWithKey<IToRequired, 'a'>

// 去除必填类型后key组成的联合类型
type ExcludeRequiredToUnionKey<T> = {
    [P in keyof T]-?:  {} extends Pick<T, P> ? P : never
}[keyof T]
type ExcludeRequiredToUnionDemo = ExcludeRequiredToUnionKey<IToRequired>


//  去除必填类型
// type ExcludeRequired<T> = {
//     [P in ExcludeRequiredToUnionKey<T>]: T[P]
// }
// or
type ExcludeRequired<T> = Pick<T, ExcludeRequiredToUnionKey<T>>
type ExcludeRequiredDemo = ExcludeRequired<IToRequired>

Readonly

interface IToReadonly {
    readonly a: string
    b: number | undefined
    c?: string
    d: number;
    e?: symbol
}
// type ToReadonly<T> = Readonly<T>
type ToReadonly<T> = {
    readonly [K in keyof T]: T[K]
}
type ToReadonlyDemo = ToReadonly<IToReadonly>


// 指定字段为只读
// type ToReadonlyWithKey<T, K extends keyof T> = Readonly<Pick<T, K>> & Omit<T, K>
type ToReadonlyWithKey<T, K extends keyof T> = {
    readonly [P in K]: T[P]
} & {
    [P in Exclude<keyof T, K>]: T[P]
} 
type ToReadonlyWithKeyDemo = ToReadonlyWithKey<IToReadonly, 'a'>



// 去除只读类型后key组成的联合类型
// https://github.com/Microsoft/TypeScript/issues/27024#issuecomment-421529650
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? A : B;

type ExcludeReadonlyToUnionKey<T> = {
    [P in keyof T]-?: IfEquals<{[Q in P]: T[P]}, {-readonly [Q in P]: T[P]}, P, never>
}[keyof T]
type ExcludeReadonlyToUnionKeyDemo = ExcludeReadonlyToUnionKey<IToReadonly>

//  去除只读类型
// type ExcludeReadonly<T> = {
//     [P in ExcludeReadonlyToUnionKey<T>]: T[P]
// }
// or
type ExcludeReadonly<T> = Pick<T, ExcludeReadonlyToUnionKey<T>>
type ExcludeReadonlyDemo = ExcludeReadonly<IToReadonly>

参考文章

juejin.cn/post/692750…

mp.weixin.qq.com/s/wLTCyRhXX…

zhuanlan.zhihu.com/p/104565681

fettblog.eu/typescript-…

mp.weixin.qq.com/s/-x8iVK-hl…

github.com/type-challe…