TS 类型挑战

按照这个库type-challengesopen in new window的题目,学习编写 TS 类型

Easy 题

实现 Pick

题目:通过从 K 中选择属性 T 来构造类型

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

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

const todo: TodoPreview = {
  title: 'Clean room',
  completed: false,
}
1
2
3
4
5
6
7
8
9
10
11
12

解答:

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

实现 Readonly

题目:构造一个类型,并将 T 的所有属性设置为只读,这意味着无法重新分配所构造类型的属性。

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
1
2
3
4
5
6
7
8
9
10
11
12

解答:

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

元组转换为对象

题目:给定数组,转换为对象类型,键/值必须在给定数组中。

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

const result: TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
1
2
3

解答:

type TupleToObject<T> = {
  [P in T[number]]: P
}
1
2
3

第一个元素

题目:实现一个通用First<T>,它接受一个数组T并返回它的第一个元素的类型。

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
1
2
3
4
5

解答:

type First<T extends any[]> = T extends [] ? never : T[0]
1

获取元组长度

题目:对于给定的元组,您需要创建一个通用的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
1
2
3
4
5

解答:

type Length<T extends readonly any[]> = T['length']
1

Exclude

题目:实现内置的Exclude <T,U>,从 T 中排除可分配给 U 的那些类型

type x = string | number | boolean

type y = string | number

type c = MyExclude<x, y>

const b: c = true
1
2
3
4
5
6
7

解答:

type MyExclude<T, U> = T extends U ? never : T
1

Awaited

题目:我们有 Promise<ExampleType> 如何获得 ExampleType

type b = Promise<string>

const c: Awaited<b> = ''
1
2
3

解答:

type MyAwaited<T extends Promise<any>> = T extends Promise<infer R> ? (R extends Promise<any> ? MyAwaited<R> : R) : never
1

If

题目:实现一个工具类型,它接受条件 c 为 true 或 false,c 为 true 返回 T,c 为 false 返回 U。

type A = If<true, 'a', 'b'> // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'
1
2

解答:

type If<C extends boolean, T, U> = C extends true ? T : U
1

Concat

题目:在类型系统中实现Array.concat

type Result = Concat<[1], [2]> // expected to be [1, 2]
1

解答:

type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]
1

Includes

题目:在类型系统中实现Array.includes

type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
1

解答:

type Includes<T extends readonly any[], U> = T extends [infer F, ...infer R] ? (Equal<U, F> extends true ? true : Includes<R, U>) : false
1

Push

题目:在类型系统中实现Array.push

type Result = Push<[1, 2], '3'> // [1, 2, '3']
1

解答:

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

Unshift

题目:在类型系统中实现Array.unshift

type Result = Unshift<[1, 2], 0> // [0, 1, 2,]
1

解答:

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

Parameters

题目:实现内置的 Parameters 类型

const foo = (arg1: string, arg2: number): void => {}
type Result = MyParameters<typeof foo> // [string, number]
1
2

解答:

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

Middle 题

获取函数返回类型

题目:不使用 ReturnType 实现 TypeScript 的 ReturnType<T> 范型。

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

type a = MyReturnType<typeof fn> // 应推导出 "1 | 2"
1
2
3
4
5
6

解答:

type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any
1

实现 Omit

题目:不使用 Omit 实现 TypeScript 的 Omit<T, K> 范型。Omit 会创建一个省略 K 中字段的 T 对象。

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

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

const todo: TodoPreview = {
  completed: false,
}
1
2
3
4
5
6
7
8
9
10
11

解答:

type MyOmit<T, U extends keyof T> = {
  [P in Exclude<keyof T, U>]: T[P]
}
1
2
3

Readonly 2

题目:实现一个通用MyReadonly2<T, K>,它带有两种类型的参数 T 和 K。

K 指定应设置为 Readonly 的 T 的属性集。如果未提供 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

解答:

type MyReadonly2<T, K extends keyof T = keyof T> = {
  readonly [P in K]: T[P]
} & {
  [P in Exclude<keyof T, K>]: T[P]
}
1
2
3
4
5

深度 Readonly

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

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

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

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

const todo: DeepReadonly<X> // should be same as `Expected`
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

解答:

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

元组转合集

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

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

const a: TupleToUnion<Arr> // expected to be '1' | '2' | '3'
1
2
3

解答:

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

可串联构造器

题目:在 JavaScript 中我们很常会使用可串联(Chainable/Pipeline)的函数构造一个对象,但在 TypeScript 中,你能合理的给他附上类型吗?

在这个挑战中,你可以使用任意你喜欢的方式实现这个类型 - Interface, Type 或 Class 都行。你需要提供两个函数 option(key, value) 和 get()。在 option 中你需要使用提供的 key 和 value 扩展当前的对象类型,通过 get 获取最终结果。

你只需要在类型层面实现这个功能 - 不需要实现任何 TS/JS 的实际逻辑。

你可以假设 key 只接受字符串而 value 接受任何类型,你只需要暴露它传递的类型而不需要进行任何处理。同样的 key 只会被使用一次。

declare const config: Chainable

const result = config.option('foo', 123).option('name', 'type-challenges').option('bar', { value: 'Hello World' }).get()

// 期望 result 的类型是:
interface Result {
  foo: number
  name: string
  bar: {
    value: string
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

解答:

type Chainable<T = {}> = {
  option<k extends string, v>(key: k extends keyof T ? never : k, value: v): Chainable<T & { [i in k]: v }>
  get(): T
}
1
2
3
4

最后一个元素

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

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

type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1
1
2
3
4
5

解答:

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

type Last<T> = T extends [...any[], infer P] ? P : never
1
2
3
4

出堆

题目:实现一个通用Pop<T>,它接受一个数组 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]
1
2
3
4
5

解答:

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

Promise.all

题目:键入函数PromiseAll,它接受 PromiseLike 对象数组,返回值应为Promise<T>,其中 T 是解析的结果数组。

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

// expected to be `Promise<[number, number, string]>`
const p = PromiseAll([promise1, promise2, promise3] as const)
1
2
3
4
5
6
7
8

解答:

declare function PromiseAll<T extends any[]>(
  values: readonly [...T]
): Promise<{
  [P in keyof T]: T[P] extends Promise<infer U> ? U : T[P]
}>
1
2
3
4
5

Type Lookup

题目:有时,您可能希望根据其属性在并集中查找类型。

在此挑战中,我们想通过在联合Cat | Dog中搜索公共 type 字段来获取相应的类型。换句话说,在以下示例中,我们期望LookUp<Dog | Cat, 'dog'>获得 Dog,LookUp<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`
1
2
3
4
5
6
7
8
9
10
11
12

解答:

type LookUp<U, T extends string> = U extends { type: T } ? U : never
1

Trim Left

题目:删除字符串开头的空格

type trimed = TrimLeft<'  Hello World  '> // expected to be 'Hello World  '
1

解答:

type TrimLeft<S extends string> = S extends `${' ' | '\n' | '\t'}${infer R}` ? TrimLeft<R> : S
1

Trim

题目:删除字符串开头和结尾的空格

type trimed = Trim<'  Hello World  '> // expected to be 'Hello World'
1

解答:

type space = ' ' | '\n' | '\t'
type Trim<S extends string> = S extends `${space}${infer R}` ? Trim<R> : S extends `${infer R}${space}` ? Trim<R> : S
1
2

Capitalize

题目:将第一个字符转为大写

type capitalized = Capitalize<'hello world'> // expected to be 'Hello world'
1

解答:

type MyCapitalize<S extends string> = S extends `${infer U}${infer R}` ? `${Uppercase<U>}${R}` : S
1

Replace

题目:替换给定的内容

type replaced = Replace<'types are fun!', 'fun', 'awesome'> // expected to be 'types are awesome!'
1

解答:

type Replace<S extends string, From extends string, To extends string> = From extends ''
  ? S
  : S extends `${infer A}${From}${infer B}`
  ? `${A}${To}${B}`
  : S
1
2
3
4
5

ReplaceAll

题目:替换全部给定的内容

type replaced = ReplaceAll<'t y p e s', ' ', ''> // expected to be 'types'
1

解答:

type ReplaceAll<S extends string, From extends string, To extends string> = From extends ''
  ? S
  : S extends `${infer R}${From}${infer U}`
  ? `${R}${To}${ReplaceAll<`${U}`, From, To>}`
  : S
1
2
3
4
5

追加参数

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

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

type Result = AppendArgument<Fn, boolean>
// 期望是 (a: number, b: string, x: boolean) => number
1
2
3
4

解答:

type AppendArgument<Fn, A> = Fn extends (...arg: infer P) => infer R ? (...arg: [...P, A]) => R : never
1

Permutation

实现联合类型的全排列,将联合类型转换成所有可能的全排列数组的联合类型。

type perm = Permutation<'A' | 'B' | 'C'> // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']
1

解答:

type Permutation<T, U = T> = [T] extends [never] ? [] : T extends U ? [T, ...Permutation<Exclude<U, T>>] : []
1

Length of String

题目:计算字符串的长度

type a = 'hellow world'

type b = LengthOfString<a> // type b = 12
1
2
3

解答:

type LengthOfString<T extends string, U extends any[] = []> = T extends `${infer R}${infer K}` ? LengthOfString<K, [...U, R]> : U['length']
1

Flatten

题目:铺平数组

type flatten = Flatten<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, 5]
1

解答:

type Flatten<T extends any[]> = T extends [infer R, ...infer K]
  ? R extends any[]
    ? [...Flatten<R>, ...Flatten<K>]
    : [R, ...Flatten<K>]
  : T
1
2
3
4
5

Append to object

题目:拓展对象的属性

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

解答:

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

Absolute

题目:获取数字的绝对值,返回绝对值的字符串形式

type Test = -100
type Result = Absolute<Test> // expected to be "100"
1
2

解答:

type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer R}` ? R : `${T}`
1

String to Union

题目:实现一个将接收到的 String 参数转换为一个字母 Union 的类型。

type Test = '123'
type Result = StringToUnion<Test> // expected to be "1" | "2" | "3"
1
2

解答:

type StringToUnion<T extends string> = T extends `${infer R}${infer U}` ? R | StringToUnion<U> : never
1

Merge

题目:合并两个类型,key 相同的类型由第二个覆盖第一个

type a = {
  x: 1
  y: 3
}

type b = {
  y: 2
  z: 3
}

type c = Merge<a, b> // c { x: 1, y: 2, z: 3 }
1
2
3
4
5
6
7
8
9
10
11

解答:

type Merge<F, S> = {
  [P in keyof F | keyof S]: P extends keyof S ? S[P] : P extends keyof F ? F[P] : never
}
1
2
3

KebabCase

题目: FooBarBaz -> foo-bar-baz

type a = 'forBarBaz'

type b = KebabCase<a> // for-bar-baz
1
2
3

解答:

type KebabCase<S> = S extends `${infer R}${infer U}` ? `${Lowercase<R>}${U extends Uncapitalize<U> ? KebabCase<U> : `-${KebabCase<U>}`}` : S
1

Diff

题目:获取两个接口类型中的差值属性。

type Foo = {
  a: string
  b: number
}
type Bar = {
  a: string
  c: boolean
}

type Result1 = Diff<Foo, Bar> // { b: number, c: boolean }
type Result2 = Diff<Bar, Foo> // { b: number, c: boolean }
1
2
3
4
5
6
7
8
9
10
11

解答:

type Diff<O, O1> = {
  [P in Exclude<keyof O, keyof O1> | Exclude<keyof O1, keyof O>]: P extends keyof O ? O[P] : P extends keyof O1 ? O1[P] : never
}
1
2
3

AnyOf

题目: 在类型系统中实现类似于 Python 中 any 函数。类型接收一个数组,如果数组中任一个元素为真,则返回 true,否则返回 false。如果数组为空,返回 false

type Sample1 = AnyOf<[1, '', false, [], {}]> // expected to be true.
type Sample2 = AnyOf<[0, '', false, [], {}]> // expected to be false.
1
2

解答:

type AnyOf<T extends readonly any[]> = T extends Array<0 | '' | false | [] | Record<PropertyKey, never>> ? false : true
1

IsNever

题目: 判断是否为 never 类型

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
1
2
3
4
5

解答:

type IsNever<T> = [T] extends [never] ? true : false
1

IsUnion

题目: 判断是否为联合类型

type case1 = IsUnion<string> // false
type case2 = IsUnion<string | number> // true
type case3 = IsUnion<[string | number]> // false
1
2
3

解答:

type IsUnion<T, K = T> = [T] extends [never] ? false : T extends K ? ([K] extends [T] ? false : true) : never
1

ReplaceKeys

题目: 根据指定的 key 替换属性

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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

解答:

type ReplaceKeys<U, T, Y> = U extends any
  ? {
      [P in keyof U]: P extends T ? (P extends keyof Y ? Y[P] : never) : U[P]
    }
  : never
1
2
3
4
5

Percentage Parser

题目: 实现类型 PercentageParser<T>。根据规则 /^(\+|\-)?(\d*)?(\%)?$/ 匹配类型 T。

匹配的结果由三部分组成,分别是:[正负号, 数字, 单位],如果没有匹配,则默认是空字符串。

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", ""]
1
2
3
4
5
6
7
8
9
10
11

解答:

type ParserLeft<A> = A extends `${infer R}${infer U}` ? (R extends '+' | '-' ? R : '') : ''
type ParserRight<A> = A extends `${infer R}${'%'}` ? '%' : ''
type PercentageParser<A extends string> = [ParserLeft<A>, A extends `${ParserLeft<A>}${infer R}${ParserRight<A>}` ? R : '', ParserRight<A>]
1
2
3

Drop Char

题目: 从字符串中剔除指定字符。

type Butterfly = DropChar<' b u t t e r f l y ! ', ' '> // 'butterfly!'
1

解答:

type DropChar<S, C> = S extends `${infer R}${infer U}` ? `${R extends C ? '' : R}${DropChar<U, C>}` : S
1

PickByType

题目:根据指定值筛选出符合的字段。

type OnlyBoolean = PickByType<
  {
    name: string
    count: number
    isReadonly: boolean
    isEnable: boolean
  },
  boolean
> // { isReadonly: boolean; isEnable: boolean; }
1
2
3
4
5
6
7
8
9

解答:

type PickByType<T, U> = {
  [P in keyof T as T[P] extends U ? P : never]: T[P]
}
1
2
3

StartsWith

题目:实现StartsWith<T, U>,接收两个 string 类型参数,然后判断T是否以U开头,根据结果返回truefalse

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
1
2
3

解答:

type StartsWith<T extends string, U extends string> = T extends `${U}${infer R}` ? true : false

type EndsWith<T extends string, U extends string> = T extends `${infer R}${U}` ? true : false
1
2
3

PartialByKeys

题目: 实现一个通用的PartialByKeys<T, K>,它接收两个类型参数TK

K指定应设置为可选的T的属性集。当没有提供K时,它就和普通的Partial<T>一样使所有属性都是可选的。

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

type UserPartialName = PartialByKeys<User, 'name'> // { name?:string; age:number; address:string }
1
2
3
4
5
6
7

解答:

type PartialByKeys<T, U extends PropertyKey = keyof T> = Partial<T> & Omit<T, U> extends infer R
  ? {
      [K in keyof R]: R[K]
    }
  : never
1
2
3
4
5

RequiredByKeys

题目: 实现一个通用的RequiredByKeys<T, K>,它接收两个类型参数TK

K指定应设为必选的T的属性集。当没有提供K时,它就和普通的Required<T>一样使所有的属性成为必选的。

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

type UserRequiredName = RequiredByKeys<User, 'name'> // { name: string; age?: number; address?: string }
1
2
3
4
5
6
7

解答:

type RequiredByKeys<T, U extends PropertyKey = keyof T> = T & Required<Pick<T, U extends keyof T ? U : never>> extends infer R
  ? { [K in keyof R]: R[K] }
  : never
1
2
3

Mutable

题目: 实现一个通用的类型 Mutable<T>,使类型 T 的全部属性可变(非只读)。

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

type MutableTodo = Mutable<Todo> // { title: string; description: string; completed: boolean; }
1
2
3
4
5
6
7

解答:

type Mutable<T extends object> = {
  -readonly [P in keyof T]: T[P]
}
1
2
3

OmitByType

题目: 根据指定类型排除属性

type OmitBoolean = OmitByType<
  {
    name: string
    count: number
    isReadonly: boolean
    isEnable: boolean
  },
  boolean
> // { name: string; count: number }
1
2
3
4
5
6
7
8
9

解答:

type OmitByType<T, U> = {
  [P in keyof T as T[P] extends U ? never : P]: T[P]
}
1
2
3

ObjectEntries

题目: 实现Object.entries

interface Model {
  name: string
  age: number
  locations: string[] | null
}
type modelEntries = ObjectEntries<Model> // ['name', string] | ['age', number] | ['locations', string[] | null];
1
2
3
4
5
6

解答:

type ObjectEntries<T, K extends keyof T = keyof T> = K extends keyof T
  ? [K, T[K] extends undefined ? undefined : Exclude<T[K], undefined>]
  : never
1
2
3

Shift

题目: 实现Array.shift

type Result = Shift<[3, 2, 1]> // [2, 1]
1

解答:

type Shift<T extends any[]> = T extends [infer R, ...infer U] ? [...U] : never
1

Tuple to Nested Object

题目: 将数组转为嵌套的对象

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
1
2
3

解答:

type TupleToNestedObject<T extends any[], U> = T extends [infer R extends PropertyKey, ...infer K] ? { [key in R]: TupleToNestedObject<K, U> } : U
1

Reverse

题目: 实现Array.reverse

type a = Reverse<['a', 'b']> // ['b', 'a']
type b = Reverse<['a', 'b', 'c']> // ['c', 'b', 'a']
1
2

解答:

type Reverse<T extends any[]> = T extends [...infer R, infer U] ? [U, ...Reverse<R>] : T
1

Flip Arguments

题目: 返回一个反转了参数的函数类型

type Flipped = FlipArguments<(arg0: string, arg1: number, arg2: boolean) => void>
// (arg0: boolean, arg1: number, arg2: string) => void
1
2

解答:

type FlipArguments<T extends (...arg: any[]) => any> = T extends (...arg: infer R) => infer S ? (...arg: Reverse<R>) => S : T
1

FlattenDepth

题目: 根据给定值对数组执行 Flatten 操作,默认 Flatten 一层

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
1
2

解答:

type needFlatten<T extends any[]> = T extends [infer U, ...infer R] ? (U extends any[] ? true : needFlatten<R>) : false

type Flatten<T extends any[]> = T extends [infer U, ...infer R] ? (U extends any[] ? [...U, ...Flatten<R>] : [U, ...Flatten<R>]) : []

type FlattenDepth<T extends any[], U extends number = 1, arr extends any[] = []> = needFlatten<T> extends false
  ? T
  : arr['length'] extends U
  ? T
  : FlattenDepth<Flatten<T>, U, [...arr, any]>
1
2
3
4
5
6
7
8
9

BEM style string

题目: 给定参数返回类名的组合

type ClassNames1 = BEM<'btn', ['price']> // 'btn__price'
type ClassNames2 = BEM<'btn', ['price'], ['warning', 'success']> // 'btn__price--warning' | 'btn__price--success'
type ClassNames3 = BEM<'btn', [], ['small', 'medium', 'large']> // 'btn--small' | 'btn--medium' | 'btn--large'
1
2
3

解答:

type isNever<T> = T extends [never] ? true : false
type safeString<T> = isNever<T> extends true ? '' : T
type BEM<B extends string, E extends string[], M extends string[]> = `${B}${safeString<`__${E[number]}`>}${safeString<`--${M[number]}`>}`
1
2
3

Flip

题目: 反转对象的 key 和 value

Flip<{ a: "x", b: "y", c: "z" }>; // {x: 'a', y: 'b', z: 'c'}
Flip<{ a: 1, b: 2, c: 3 }>; // {1: 'a', 2: 'b', 3: 'c'}
Flip<{ a: false, b: true }>; // {false: 'a', true: 'b'}
1
2
3

解答:

type Flip<T extends { [key: PropertyKey]: any }> = {
  [P in keyof T as `${T[P]}`]: P
}
1
2
3

Zip

题目: 合并两个数组,将两个数组都具有的项合并到同一项,其他的不要

type a = Zip<[1, 2], [true, false]> // expected to be [[1, true], [2, false]]
type b = Zip<[1, 2, 3], ['1', '2']> //  [[1, '1'], [2, '2']]
1
2

解答:

type Zip<T extends any[], U extends any[]> = T extends [infer Pt, ...infer Rt]
  ? U extends [infer Pu, ...infer Ru]
    ? [[Pt, Pu], ...Zip<Rt, Ru>]
    : []
  : []
1
2
3
4
5

IsTuple

题目: 判断当前类型是否为元组

type case1 = IsTuple<[number]> // true
type case2 = IsTuple<readonly [number]> // true
type case3 = IsTuple<number[]> // false
1
2
3

解答:

type IsTuple<T> = [T] extends [never] ? false : T extends [any] | [] | readonly [any] ? true : false
1

Chunk

题目: 按照指定的数量将数组划分

type exp1 = Chunk<[1, 2, 3], 2> // expected to be [[1, 2], [3]]
type exp2 = Chunk<[1, 2, 3], 4> // expected to be [[1, 2, 3]]
type exp3 = Chunk<[1, 2, 3], 1> // expected to be [[1], [2], [3]]
1
2
3

解答:

type Chunk<T extends any[], N extends number, U extends any[] = []> = T extends [infer P, ...infer R]
  ? U['length'] extends N
    ? [U, ...Chunk<T, N>]
    : Chunk<R, N, [...U, P]>
  : U extends []
  ? []
  : [U]
1
2
3
4
5
6
7

Without

题目: 实现一个像 Lodash.without 函数一样的泛型 Without<T, U>,它接收数组类型的 T 和数字或数组类型的 U 为参数,会返回一个去除 U 中元素的数组 T。

type Res = Without<[1, 2], 1> // expected to be [2]
type Res1 = Without<[1, 2, 4, 1, 5], [1, 2]> // expected to be [4, 5]
type Res2 = Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]> // expected to be []
1
2
3

解答:

type Without<T, U> = T extends [infer P, ...infer R]
  ? P extends T[number] & (U extends any[] ? U[number] : U)
    ? [...Without<R, U>]
    : [P, ...Without<R, U>]
  : []
1
2
3
4
5

Trunc

题目: 实现Math.Trunc

type A = Trunc<12.34> // 12
1

解答:

type Trunc<T extends string | number> = `${T}` extends `${infer U}.${infer R}` ? `${U}` : `${T}`
1

IndexOf

题目: 实现Array.indexOf

type Res = IndexOf<[1, 2, 3], 2> // expected to be 1
type Res1 = IndexOf<[2, 6, 3, 8, 4, 1, 7, 3, 9], 3> // expected to be 2
type Res2 = IndexOf<[0, 0, 0], 2> // expected to be -1
1
2
3

解答:

type IndexOf<T extends any[], U, P extends any[] = []> = T extends [infer K, ...infer R]
  ? Equal<K, U> extends true
    ? P['length']
    : IndexOf<R, U, [...P, K]>
  : -1
1
2
3
4
5

Join

题目: 实现Array.join

type Res = Join<['a', 'p', 'p', 'l', 'e'], '-'> // expected to be 'a-p-p-l-e'
type Res1 = Join<['Hello', 'World'], ' '> // expected to be 'Hello World'
type Res2 = Join<['2', '2', '2'], 1> // expected to be '21212'
type Res3 = Join<['o'], 'u'> // expected to be 'o'
1
2
3
4

解答:

type Join<T extends unknown[], U extends string | number> = T extends [infer P, ...infer R]
  ? R['length'] extends 0
    ? P
    : `${P & string}${U}${Join<R, U>}`
  : ''
1
2
3
4
5

lastIndexOf

题目: 实现Array.lastIndexOf

type Res1 = LastIndexOf<[1, 2, 3, 2, 1], 2> // 3
type Res2 = LastIndexOf<[0, 0, 0], 2> // -1
1
2

解答:

type LastIndexOf<T, U> = T extends [...infer R, infer P] ? (Equal<U, P> extends true ? R['length'] : LastIndexOf<R, U>) : -1
1

Unique

题目: 数组去重

type Res = Unique<[1, 1, 2, 2, 3, 3]> // expected to be [1, 2, 3]
type Res1 = Unique<[1, 2, 3, 4, 4, 5, 6, 7]> // expected to be [1, 2, 3, 4, 5, 6, 7]
type Res2 = Unique<[1, 'a', 2, 'b', 2, 'a']> // expected to be [1, "a", 2, "b"]
type Res3 = Unique<[string, number, 1, 'a', 1, string, 2, 'b', 2, number]> // expected to be [string, number, 1, "a", 2, "b"]
type Res4 = Unique<[unknown, unknown, any, any, never, never]> // expected to be [unknown, any, never]
1
2
3
4
5

解答:

type Unique<T extends any[], U extends any[] = []> = T extends [infer P, ...infer R]
  ? Includes<U, P> extends true
    ? Unique<R, U>
    : Unique<R, [...U, P]>
  : U
1
2
3
4
5

MapTypes

题目: 按照给定的类型进行转换

type StringToNumber = { mapFrom: string; mapTo: number }
type StringToDate = { mapFrom: string; mapTo: Date }
type A = MapTypes<{ iWillBeNumberOrDate: string }, StringToDate | StringToNumber> // gives { iWillBeNumberOrDate: number | Date; }
1
2
3

解答:

type MapTypes<T extends Record<PropertyKey, unknown>, U extends { mapFrom: unknown; mapTo: unknown }> = {
  [P in keyof T]: T[P] extends U['mapFrom'] ? Extract<U, { mapFrom: T[P] }>['mapTo'] : T[P]
}
1
2
3

Construct Tuple

题目: 构造数组

type result = ConstructTuple<2> // expect to be [unknown, unkonwn]
1

解答:

type ConstructTuple<T extends number, U extends any[] = []> = T extends U['length'] ? U : ConstructTuple<T, [...U, unknown]>
1