文章目录
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<[]>, []>>,