Skip to content
Go back

Typescript类型体操

Updated:  at  10:50 PM

文章目录

Pick

type MyPick<T, K extends keyof T> = {
  [key in K]: T[key]
}

interface Todo {
 title: string
 description: string
 completed: boolean
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {
  title: 'Clean room',
  completed: false,
}

Readonly

type MyReadonly<T> = {
  readonly [K in keyof T]: T[K]
}

interface Todo {
 title: string
 description: string
}

const todo: MyReadonly<Todo> = {
 title: "Hey",
 description: "foobar"
}

todo.title = "Hello" // Error: cannot reassign a readonly property

TupleToObject


type TupleToObject<T extends readonly PropertyKey[]> = {
  [K in T[number]]: K
}

// type PropertyKey = string | number | symbol;

const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const

type result = TupleToObject<typeof tuple> // expected { 'tesla': 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

First Element

// type First<T extends any[]> = T extends []? never:T[0];
// type First<T extends any[]> = T['length'] extends 0 ? never : T[0];
type First<T extends any[]> = T extends [infer A, ...infer rest] ? A : never;

type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]

type head1 = First<arr1> // 应推导出 'a'
type head2 = First<arr2> // 应推导出 3

The length of Array

type Length<U extends readonly any[]> = U['length'];

type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']

type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5

Exclude

type MyExclude<T, U> = T extends U ? never : T
type A = 's' | 'n' | 'q'
type B = 's' | 'n'
type C = A extends B ? never : A // 's' | 'n' | 'q'
type D = MyExclude<A, B> // 'q'

// In type C, A is not a generic type; it's a union of string literals ('s' | 'n' | 'q'). When you use a non-generic type in a conditional type, it is not distributive, and the condition is evaluated against the entire type as a whole. So, in C, you are checking if the entire type A extends the entire type B. Since 'q' is not in B, it's included in the result.

// In type D, you are using a generic conditional type MyExclude<T, U>, and when you apply this type to the types A and B, it becomes distributive. For each element in the union type A, the condition is checked separately. So, 's' and 'n' are excluded individually, and 'q' is not excluded, resulting in the type 'q'.

// In summary, the difference is due to how conditional types behave when applied to non-generic and generic types. Non-generic types are not distributive, while generic types can be distributive, considering each element of the union type individually.

Awaited

type MyAwaited<T> = T extends PromiseLike<U> ? U extends PromiseLike<any> ? MyAwaited<U> : U : never ;

type X = Promise<string> // string
type Y = Promise<{ field: number }> // { field: number }
type Z = Promise<Promise<string | number>> // string | number
type Z1 = Promise<Promise<Promise<string | boolean>>> // string | boolean
type T = { then: (onfulfilled: (arg: number) => any) => any } // number

If


type If<C extends Boolean, T, F> = C extends true ? T : F;

If<true, 'a', 'b'> // 'a'
If<false, 'a', 2> // 2
If<null, 'a', 'b'> // error

Concat


type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U]

Expect<Equal<Concat<[], []>, []>>,
Expect<Equal<Concat<[], [1]>, [1]>>,
Expect<Equal<Concat<typeof tuple, typeof tuple>, [1, 1]>>,
Expect<Equal<Concat<[1, 2], [3, 4]>, [1, 2, 3, 4]>>,
Expect<Equal<Concat<['1', 2, '3'], [false, boolean, '4']>, ['1', 2, '3', false, boolean, '4']>>,

Includes


type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest] ? Equal<U, First> extends true ? true : Includes<Rest, U> : false;

Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7, false], false>, true>>

Equal


type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false

Equal<true, false>;

Push


type Push<T extends any[], U> = [...T, U];

UnShift


type Unshift<T extends any[], U> = [U, ...T ];

MyParameters


type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never;

MyReturnType


type MyReturnType<T extnds Function> = T extends (...args:infer U) => any? U : never

Omit

type MyOmit<T, K extends keyof T> = {
  [G in keyof T as G extends K ? never : G]: T[G]
}

interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyOmit<Todo, 'description' | 'title'>

const todo: TodoPreview = {
  completed: false,
}

Readonly2

// type MyReadonly2<T, K extends keyof T = keyof T> = {
//   readonly [k in K]: T[k]; } & {
//   [G in keyof T as G extends K ? never : G]: T[G];
// }

type MyReadonly2<T, K extends keyof T = keyof T> = Omit<T, K> & {
  readonly [k in K]: T[k];
}

Expect<Alike<MyReadonly2<Todo1>, Readonly<Todo1>>>,
Expect<Alike<MyReadonly2<Todo1, 'title' | 'description'>, Expected>>,
Expect<Alike<MyReadonly2<Todo2, 'title' | 'description'>, Expected>>,
Expect<Alike<MyReadonly2<Todo2, 'description'>, Expected>>,

interface Todo1 {
  title: string
  description?: string
  completed: boolean
}

interface Todo2 {
  readonly title: string
  description?: string
  completed: boolean
}

interface Expected {
  readonly title: string
  readonly description?: string
  completed: boolean
}

DeepReadonly


// type DeepReadonly<T> = T extends never | Function ? T : { readonly [K in keyof T]: DeepReadonly<T[K]> }

type DeepReadonly<T> = T extends any ? {
  readonly [P in keyof T]: keyof T[P] extends never ? T[P] : DeepReadonly<T[P]>
} : never


type X = { 
  x: { 
    a: 1
    b: 'hi'
  }
  y: 'hey'
}

type Expected = {
  readonly x: { 
    readonly a: 1
    readonly b: 'hi'
  }
  readonly y: 'hey' 
}

type Todo = DeepReadonly<X> // should be same as `Expected`

let a = () => 1 type b = keyof typeof a; // never

TupleToUnion

// type TupleToUnion<T> = T extends [infer D, ...infer T] ? D | TupleToUnion<T> : never;

type TupleToUnion<T extends any[]> = T[number]

Expect<Equal<TupleToUnion<[123, '456', true]>, 123 | '456' | true>>,
Expect<Equal<TupleToUnion<[123]>, 123>>,

Chainable Option

Last of Array

type Last< T extends any[]> = T extends [...infer reset, infer last]? last: T

type Last< T extends any[]> = [never, ...T][T['length']];

Expect<Equal<Last<[]>, never>>,
Expect<Equal<Last<[2]>, 2>>,
Expect<Equal<Last<[3, 2, 1]>, 1>>,
Expect<Equal<Last<[() => 123, { a: string }]>, { a: string }>>,

Pop

type Pop<T extends any[]> = T extends [...infer U, infer last]? U : T

Expect<Equal<Pop<[3, 2, 1]>, [3, 2]>>,
Expect<Equal<Pop<['a', 'b', 'c', 'd']>, ['a', 'b', 'c']>>,
Expect<Equal<Pop<[]>, []>>,


Previous Post
docker常用命令
Next Post
Typescript