原网站

简单(easy)

pick

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

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

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
}
说明
type Pick<T extends Object, K extends keyof T> = {
  [P in K]: T[P]
}

ReadOnly

interface Todo {
  title: string
  description: string
}

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

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
说明
type ReadOnly<T extends Object> = {
  readonly [P in keyof T]: T[P]
}

元组转换成对象

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'}
说明
type TupleToObject<T extends readonly string[]> = {
  [P in T[number]]: P
}

第一个元素

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

type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
查看答案
type First<T extends any[]> = T extends [infer Result,...infer Rest] ? Result : never;

获取元素长度

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
查看答案
type Length<T extends readonly any[]> = T['length'];

Exclude

/** 
 * 实现内置的Exclude <T,U>类型,但不能直接使用它本身。
 * 从联合类型T中排除U的类型成员,来构造一个新的类型。
*/
查看答案
type Exclude<T, U> = T extends U ? never : T

Awaited

假如我们有一个 Promise 对象,这个 Promise 对象会返回一个类型。在 TS 中,我们用 Promise 中的 T 来描述这个 Promise 返回的类型。请你实现一个类型,可以获取这个类型。

比如:Promise<ExampleType>,请你返回 ExampleType 类型。
查看答案
type MyAwaited<T> = T extends Promise<infer P> ? MyAwaited<P> : T;

IF

实现一个 IF 类型,它接收一个条件类型 C ,一个判断为真时的返回类型 T ,以及一个判断为假时的返回类型 FC 只能是 true 或者 falseTF 可以是任意类型。

type A = If<true, 'a', 'b'>  // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'
查看答案
type If<C extends boolean, T, F> = T extends true ? T : F;

Concat

在类型系统里实现 JavaScript 内置的 Array.concat 方法,这个类型接受两个参数,返回的新数组类型应该按照输入参数从左到右的顺序合并为一个新的数组。

type Result = Concat<[1], [2]> // expected to be [1, 2]
查看答案
type Concat<T extends any[], P extends any[]> = [...T, ...P]

Include

在类型系统里实现 JavaScriptArray.includes 方法,这个类型接受两个参数,返回的类型要么是 true 要么是 false

type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
查看答案
type Include<T extends readonly any[], P> = T extends [infer First,...infer Other] ? (Equal<First,U> extends true ? true: Includes<Other,U>) :false 

Push

在类型系统里实现通用的 Array.push

type Result = Push<[1, 2], '3'> // [1, 2, '3']
查看答案
type Push<T extends any[], U> = [...T, U]

Unshift

实现类型版本的 Array.unshift

type Result = Unshift<[1, 2], 0> // [0, 1, 2,]
查看答案
type Unshift<T extends any[], U> = [U, ...T]

Parameters

实现内置的 Parameters 类型,而不是直接使用它,可参考TypeScript官方文档

查看答案
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;

中等

获取函数返回类型 ReturnType

不使用 ReturnType 实现 TypeScriptReturnType<T> 范型。

const fn = (v: boolean) => {
  if (v)
    return 1
  else
    return 2
}

type a = MyReturnType<typeof fn> // 应推导出 "1 | 2"
查看答案
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never

实现 omit

不使用 Omit 实现 TypeScriptOmit<T, K> 范型。

Omit 会创建一个省略 K 中字段的 T 对象。

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

type TodoPreview = MyOmit

const todo: TodoPreview = {
  completed: false,
}
查看答案
type MyPick<T, K extends keyof T> = {
  [key in K]: T[key]
}

type MyExclude<T, P> = T extends P ? never : T

type MyOmit<T, K extends keyof T> = MyPick<T, MyExclude<keyof T, K>>

Readonly 2

实现一个通用MyReadonly2<T, K>,它带有两种类型的参数TK

K指定应设置为ReadonlyT的属性集。如果未提供K,则应使所有属性都变为只读,就像普通的Readonly<T>一样。

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

const todo: MyReadonly2<Todo, 'title' | 'description'> = {
  title: "Hey",
  description: "foobar",
  completed: false,
}

todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
todo.completed = true // OK
查看答案

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

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

type MyExclude<T, K> = T extends K ? never : T;

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

 type MyReadonly2<T, K extends keyof T = keyof T> =MyReadonly<MyPick<T, K>>  & MyOmit<T, K>

深度Readonly

实现一个通用的DeepReadonly,它将对象的每个参数及其子对象递归地设为只读。

您可以假设在此挑战中我们仅处理对象。数组,函数,类等都无需考虑。但是,您仍然可以通过覆盖尽可能多的不同案例来挑战自己。

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`
查看答案

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

元组转合集

实现泛型TupleToUnion<T>,它覆盖元组的值与其值联合。

type Arr = ['1', '2', '3']

type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'
查看答案

type TupleToUnion<T extends any[]> = T extends [infer First,...infer Other] ? First | TupleToUnion<Other> : never;

// 或者

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

最后一个元素

实现一个通用Last<T>,它接受一个数组T并返回其最后一个元素的类型。

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

type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1
查看答案

type Last<T extends any[]> = T extends [...infer K, infer Other] ? Other : never

//或者
type Last<T extends any[]> = T extends [any, ...infer Rest] ? T[Rest['length']] : never

出堆

实现一个通用Pop,它接受一个数组T并返回一个没有最后一个元素的数组。

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

type re1 = Pop<arr1> // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2> // expected to be [3, 2]
查看答案

type Pop<T extends any[]> = T extends [...infer K, infer Other] ? K : never

promise.all

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

// expected to be `Promise<[number, number, string]>`
const p = Promise.all([promise1, promise2, promise3] as const)

// declare function PromiseAll(values: any): any
查看答案
// 跟上面awaited 相似
type MyAwaited<T> = T extends Promise<infer P> ? MyAwaited<P> : T;
/** declare 重定义function, (values: readonly [...T]) 函数参数 Promise<> */
declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<{
  [K in keyof T]:  MyAwaited<T[K]>
}>

Type Lookup

在此挑战中,我们想通过在联合Cat | Dog中搜索公共type字段来获取相应的类型。换句话说,在以下示例中,我们期望LookUp<Dog | Cat, 'dog'>获得DogLookUp<Dog | Cat, 'cat'>获得Cat


interface Cat {
  type: 'cat'
  breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}

interface Dog {
  type: 'dog'
  breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
  color: 'brown' | 'white' | 'black'
}

type MyDog = LookUp<Cat | Dog, 'dog'> // expected to be `Dog`
查看答案
type LookUP<T, U> = T extends { type: string } ? (T['type'] extends U ? T : never )  : never

Trim Left

实现 TrimLeft<T> ,它接收确定的字符串类型并返回一个新的字符串,其中新返回的字符串删除了原字符串开头的空白字符串。

type trimed = TrimLeft<'  Hello World  '> // 应推导出 'Hello World  '
查看答案
type TrimLet<T extends string> = T extends `${' '| '\n' | '\t'}${infer Rest}` ? TrimLeft<Rest> : T;

Trim

type trimed = Trim<'  Hello World  '> // expected to be 'Hello World'
查看答案
type SpaceChar = ' ' | '\n' | '\t'
type Trim<S extends string> = S extends (`${SpaceChar}${infer R}` | `${infer R}${SpaceChar}`) ? Trim<R> : S

Capitalize

首字母大写

type capitalized = Capitalize<'hello world'> // expected to be 'Hello world'
查看答案
type Capitalize<T extends string> = T extends (`${infer first}${infer R}`) ? `${Uppercase<first>}${R}` : T;

Replace

实现 Replace<S, From, To> 将字符串 S 中的第一个子字符串 From 替换为 To

type replaced = Replace<'types are fun!', 'fun', 'awesome'> // 期望是 'types are awesome!'
查看答案
type Replace<S extends string, From extends string, To extends string> = From extends '' ? S : S extends `${infer start}${From}${infer end}` ? `${start}${To}${end}` : S

ReplaceAll

实现 ReplaceAll<S, From, To> 将一个字符串 S 中的所有子字符串 From 替换为 To

type replaced = ReplaceAll<'t y p e s', ' ', ''> // 期望是 'types'
查看答案
/**
 * 请注意以下两种情况
 *  Expect<Equal<ReplaceAll<'foobarfoobar', 'ob', 'b'>, 'fobarfobar'>>,
 *  Expect<Equal<ReplaceAll<'foboorfoboar', 'bo', 'b'>, 'foborfobar'>>,
 */

// 错误
type ReplaceAll<S extends string, From extends string, To extends string> =  From extends '' ? S : S extends `${infer start}${From}${infer end}` ? ReplaceAll<`${start}${To}${end}`, From, To> : S

// 正确
type ReplaceAll<S extends string, From extends string, To extends string> =  From extends '' ? S : S extends `${infer start}${From}${infer end}` ? `${start}${To}${ReplaceAll<`${end}`, From, To>}` : S

追加参数

实现一个范型 AppendArgument<Fn, A>,对于给定的函数类型 Fn,以及一个任意类型 A,返回一个新的函数 GG 拥有 Fn 的所有参数并在末尾追加类型为 A 的参数。

type Fn = (a: number, b: string) => number

type Result = AppendArgument<Fn, boolean> 
// 期望是 (a: number, b: string, x: boolean) => number
查看答案
type AppendArgument<Fn extends (...args: any[]) => any, T> = Fn extends ((...args: infer P) => infer R) ? (...args: [...P, T]) => R : never 

Permutation

Implement permutation type that transforms union types into the array that includes permutations of unions.

type perm = Permutation<'A' | 'B' | 'C'>; // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']
查看答案
type Permutation<T, U = T> = [T] extends [never] ? [] : T extends U ? [T, ...Permutation<Exclude<U, T>>] : []

Length of string

Compute the length of a string literal, which behaves like String#length.

查看答案

type StringToArr<T extends String> = T extends `${infer F}${infer R}` ? [F, ...StringToArr<R>] : [];

type StringLength<T extends String> = StringToArr<T>['length']

Flatten

In this challenge, you would need to write a type that takes an array and emitted the flatten array type.

type flatten = Flatten<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, 5]
查看答案

type Flatten<T extends any[]> = T extends [infer First, ...infer Other] ? First extends any[] ? [...Flatten<First>, ...Flatten<Other>] : [First, ...Flatten<Other>] : []

Append to object

Implement a type that adds a new field to the interface. The type takes the three arguments. The output should be an object with the new field.

type Test = { id: '1' }
type Result = AppendToObject<Test, 'value', 4> // expected to be { id: '1', value: 4 }
查看答案

type AppendToObject<T extends object, U extends string, V> = { [K in keyof T | U]: K extends keyof T ? T[K] : V }

Absolute

数字绝对值

type Test = -100;
type Result = Absolute<Test>; // expected to be "100"
查看答案
type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer X}` ? X : `${T}`

String to Union

Implement the String to Union type. Type take string argument. The output should be a union of input letters

type Test = '123';
type Result = StringToUnion<Test>; // expected to be "1" | "2" | "3"
查看答案
type StringToUnion<T extends string> =  T extends `${infer F}${infer R}` ? F | StringToUnion<R> : never 

Merge

Merge two types into a new type. Keys of the second type overrides keys of the first type.

type Foo = {
  a: number;
  b: string;
};
type Bar = {
  b: number;
  c: boolean;
};
type Result = Merge<Foo, Bar>; 

// {
//     a: number;
//     b: number;
//     c: boolean;
// }
查看答案
type Merge<F, S> = {[key in keyof F | keyof S]: key extends keyof S ? S[key] : key extends keyof F ? F[key] : never};

CamelCase

for-bar-baz -> forBarBaz

查看答案
type Capitalize<T extends string> = T extends (`${infer first}${infer R}`) ? `${Uppercase<first>}${R}` : T;
type CamelCase<T extends string> = T extends `${infer start}-${infer end}` ? `${start}${CamelCase<Capitalize<end>>}` : T

type Result = CamelCase<'foo--bar----baz'> // "fooBarBaz"

type CamelCase<S extends string> =  S extends `${infer X}-${infer Y}` ? Y extends Capitalize<Y> ? `${X}-${CamelCase<Y>}` : `${X}${CamelCase<Capitalize<Y>>}` : S ;

//foo-Bar---Baz

KebabCase

FooBarBaz -> foo-bar-baz

查看答案
type KebabCase<S extends string, P extends string = ""> = 
  S extends `${infer L}${infer R}`
    ? L extends Lowercase<L> 
      ? `${L}${KebabCase<R, "-">}`
      : `${P}${Lowercase<L>}${KebabCase<R, "-">}`
    : S;

type b = KebabCase<'foo-bar'>

Diff

Get an Object that is the difference between O & O1

type Foo = {
  name: string
  age: string
}
type Bar = {
  name: string
  age: string
  gender: number
}

Expect<Equal<Diff<Foo, Bar>, { gender: number }>>
查看答案
type Diff<O, O1> = {
  [P in (keyof O | keyof O1) as Exclude<P, keyof O & keyof O1>]: P extends keyof O
    ? O[P]
    : P extends keyof O1
      ? O1[P]
      : never;
};

AnyOf

Implement Python liked any function in the type system. A type takes the Array and returns true if any element of the Array is true. If the Array is empty, return false.

type Sample1 = AnyOf<[1, "", false, [], {}]>; // expected to be true.
type Sample2 = AnyOf<[0, "", false, [], {}]>; // expected to be false.
查看答案
// solution 1
type False = 0 | '' | false | [] | null | undefined | Record<any, never>;
type AnyOf<T extends readonly any[]> = T[number] extends False ? false : true;

// solution 2
type False = 0 | '' | false | [] | null | undefined | Record<any, never>;
type AnyOf<T extends readonly any[]> = T extends [infer F, ...infer R] 
? F extends False ? AnyOf<R> : true 
: false;

isNever

Implement a type IsNever, which takes input type T. If the type of resolves to never, return true, otherwise false.

type A = IsNever<never>  // expected to be true
type B = IsNever<undefined> // expected to be false
type C = IsNever<null> // expected to be false
type D = IsNever<[]> // expected to be false
type E = IsNever<number> // expected to be false
查看答案
type isNever<T> = [T] extends [never] ? true : false

isUnion

Implement a type IsUnion, which takes an input type T and returns whether T resolves to a union type.

type case1 = IsUnion<string>  // false
type case2 = IsUnion<string|number>  // true
type case3 = IsUnion<[string|number]>  // false
查看答案

type IsUnion<T, U = T> = T extends T
  ? (U | T) extends (U & T) ? false : true
  : never

ReplaceKeys

Implement a type ReplaceKeys, that replace keys in union types, if some type has not this key, just skip replacing, A type takes three arguments.
如果

type NodeA = {
  type: 'A'
  name: string
  flag: number
}

type NodeB = {
  type: 'B'
  id: number
  flag: number
}

type NodeC = {
  type: 'C'
  name: string
  flag: number
}


type Nodes = NodeA | NodeB | NodeC

type ReplacedNodes = ReplaceKeys<Nodes, 'name' | 'flag', {name: number, flag: string}> // {type: 'A', name: number, flag: string} | {type: 'B', id: number, flag: string} | {type: 'C', name: number, flag: string} // would replace name from string to number, replace flag from number to string.

type ReplacedNotExistKeys = ReplaceKeys<Nodes, 'name', {aa: number}> // {type: 'A', name: never, flag: number} | NodeB | {type: 'C', name: never, flag: number} // would replace name to never
查看答案

type ReplaceKeys<T, K, V> = {
  [key in keyof T]: key extends K 
    ? key extends keyof V 
      ? V[key]
      : never
    : T[key]
}

Remove Index Signature

Implement RemoveIndexSignature<T>, exclude the index signature from object types.

type Foo = {
  [key: string]: any;
  foo(): void;
}

type A = RemoveIndexSignature<Foo>  // expected { foo(): void }
查看答案

type RemoveIndexSignature<T> = {
  [key in keyof T as string extends key ? never : key extends number ? never : key]: T[key]
}

Percentage Parser

Implement PercentageParser. According to the /^(\+|\-)?(\d*)?(\%)?$/ regularity to match T and get three matches.

The structure should be: [plus or minus, number, unit] If it is not captured, the default is an empty string.

type PString1 = ''
type PString2 = '+85%'
type PString3 = '-85%'
type PString4 = '85%'
type PString5 = '85'

type R1 = PercentageParser<PString1>  // expected ['', '', '']
type R2 = PercentageParser<PString2>  // expected ["+", "85", "%"]
type R3 = PercentageParser<PString3>  // expected ["-", "85", "%"]
type R4 = PercentageParser<PString4>  // expected ["", "85", "%"]
type R5 = PercentageParser<PString5>  // expected ["", "85", ""]
查看答案

type PickPrefix<T extends string, S extends string> = T extends `${S}${infer P}`
    ? T extends `${infer R}${P}` ? R : never
    : '';

type PickSuffix<T extends string, S extends string> = T extends `${infer P}${S}`
    ? T extends `${P}${infer R}` ? R : never
    : '';

type PercentageParser<T extends string> = T extends `${PickPrefix<T, '+' | '-'>}${infer B}${PickSuffix<T, '%'>}`
    ? T extends `${infer A}${B}${infer C}`
        ? [A, B, C]
        : ['', '', '']
    : ['', '', ''];

Drop Char

Drop a specified char from a string.

type Butterfly = DropChar<' b u t t e r f l y ! ', ' '> // 'butterfly!'
查看答案
type DropChar<S, C> = S extends `${infer X}${infer Y}` ? `${X extends C ? "" : X}${DropChar<Y, C>}` : ""  

MinusOne

Given a number (always positive) as a type. Your type should return the number decreased by one.

type Zero = MinusOne<1> // 0
type FiftyFour = MinusOne<55> // 54
查看答案

拓展

type Pop<T extends any[]> = T extends [...infer head, any] ? head : never; 

type MinusOne<T extends number, A extends any[] = []> = A['length'] extends T
  ? Pop<A>['length']
  : MinusOne<T, [...A, 0]>

PickByType

From T, pick a set of properties whose type are assignable to U.

type OnlyBoolean = PickByType<{
  name: string
  count: number
  isReadonly: boolean
  isEnable: boolean
}, boolean> // { isReadonly: boolean; isEnable: boolean; }
查看答案
type PickByType<T, U> = {
  [key in keyof T as T[key] extends U ? key : never]: T[key] 
}

StartsWith

Implement StartsWith<T, U> which takes two exact string types and returns whether T starts with U

type a = StartsWith<'abc', 'ac'> // expected to be false
type b = StartsWith<'abc', 'ab'> // expected to be true
type c = StartsWith<'abc', 'abcd'> // expected to be false
查看答案
type StartsWith<T extends string, U extends string> = T extends `${U}${infer Other}` ? true : false

EndsWith

Implement EndsWith<T, U> which takes two exact string types and returns whether T ends with U

type a = EndsWith<'abc', 'bc'> // expected to be true
type b = EndsWith<'abc', 'ab'> // expected to be false
查看答案
type EndsWith<T extends string, U extends string> = T extends `${infer Other}${U}` ? true : false

PartialByKeys

Implement a generic PartialByKeys<T, K> which takes two type argument T and K.

K specify the set of properties of T that should set to be optional. When K is not provided, it should make all properties optional just like the normal Partial<T>.

interface User {
  name: string
  age: number
  address: string
}

type UserPartialName = PartialByKeys<User, 'name'> // { name?:string; age:number; address:string }
查看答案
type Merge<A, B> = Pick<A & B, keyof A | keyof B>
type PartialByKeys<T , K = keyof T> = Merge<{[key in keyof T as key extends K ? key : never]?: T[key]}, {[key in keyof T as key extends K ? never : key]: T[key]}>

RequiredByKeys

Implement a generic RequiredByKeys<T, K> which takes two type argument T and K.

K specify the set of properties of T that should set to be required. When K is not provided, it should make all properties required just like the normal Required<T>.

interface User {
  name?: string
  age?: number
  address?: string
}

type UserPartialName = RequiredByKeys<User, 'name'> // { name: string; age?: number; address?: string }
查看答案
type Merge<A, B> = Pick<A & B, keyof A | keyof B>
type IsRequire<T, U> = {
  [key in keyof T as key extends U  ? key : never]-?: T[key]
}

type Origin<T, U> = {
  [key in keyof T as key extends U  ? never : key]: T[key]
}

type RequiredByKeys<T, U = keyof T> = Merge<IsRequire<T, U>, Origin<T, U>>

Mutable

Implement the generic Mutable<T> which makes all properties in T mutable (not readonly).

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

type MutableTodo = Mutable<Todo> // { title: string; description: string; completed: boolean; }
查看答案
type Mutable<T> = {
  -readonly [key in keyof T]: T[key]
}

OmitByType

From T, pick a set of properties whose type are not assignable to U.

type OmitBoolean = OmitByType<{
  name: string
  count: number
  isReadonly: boolean
  isEnable: boolean
}, boolean> // { name: string; count: number }
查看答案
type OmitByType<T, U> = {
  [key in keyof T as T[key] extends U ? never : key]: T[key]
}

ObjectEntries

Implement the type version of Object.entries

interface Model {
  name: string; 
  age: number;
  locations: string[] | null;
}
type modelEntries = ObjectEntries<Model> // ['name', string] | ['age', number] | ['locations', string[] | null];
查看答案
type ObjectEntries<T, K = keyof T> = K extends keyof T ? [K, Required<T>[K]] : never;

Shift

Implement the type version of Array.shift

type Result = Shift<[3, 2, 1]> // [2, 1]
查看答案
type Shift<T extends any[]> = T extends [infer First, ...infer Other] ? Other : never

Tuple to Nested Object

Given a tuple type T that only contains string type, and a type U, build an object recursively.

type a = TupleToNestedObject<['a'], string> // {a: string}
type b = TupleToNestedObject<['a', 'b'], number> // {a: {b: number}}
type c = TupleToNestedObject<[], boolean> // boolean. if the tuple is empty, just return the U type
查看答案
type TupleToNestedObject<T extends any[], U> = T['length'] extends 0 ? U : T extends [infer First, ...infer Other] 
  ? { [P in T[0]]: Other['length'] extends 0 ? U : TupleToNestedObject<Other, U> }
  : never

Reverse

Implement the type version of Array.reverse

type a = Reverse<['a', 'b']> // ['b', 'a']
type b = Reverse<['a', 'b', 'c']> // ['c', 'b', 'a']
查看答案
type Reverse<T extends any[]> = T extends [...infer Other, infer Last] ? [Last, ...Reverse<Other>] : []

Flip Arguments

Implement the type version of lodash’s _.flip.

Type FlipArguments<T> requires function type T and returns a new function type which has the same return type of T but reversed parameters.

type Flipped = FlipArguments<(arg0: string, arg1: number, arg2: boolean) => void> 
// (arg0: boolean, arg1: number, arg2: string) => void
查看答案
type Reverse<T extends any[]> = T extends [...infer Other, infer Last] ? [Last, ...Reverse<Other>] : []

type FlipArguments<T extends (...args: any[]) => any > = T extends (...args: infer P) => infer Q
  ? (...args: Reverse<P>) => Q
  : never;

FlattenDepth

Recursively flatten array up to depth times.

type a = FlattenDepth<[1, 2, [3, 4], [[[5]]]], 2> // [1, 2, 3, 4, [5]]. flattern 2 times
type b = FlattenDepth<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, [[5]]]. Depth defaults to be 1
查看答案

type FlattenOnce<T extends unknown[]> = T extends [infer First, ...infer Rest]
? First extends unknown[]
  ? [...First, ...FlattenOnce<Rest>]
  : [First, ...FlattenOnce<Rest>]
: [];
type t1 = FlattenOnce<[[2], [[3]], 4]>;
type FlattenDepth<T extends unknown[], N extends number = 1, C extends unknown[] = []>
= C["length"] extends N ? T
: T extends FlattenOnce<T> ? T
: FlattenDepth<FlattenOnce<T>, N, [...C, unknown]>;