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,
}
2
3
4
5
6
7
8
9
10
11
12
解答:
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
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
2
3
4
5
6
7
8
9
10
11
12
解答:
type MyReadonly<T> = {
readonly [P in keyof T]: T[P]
}
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'}
2
3
解答:
type TupleToObject<T> = {
[P in T[number]]: P
}
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
2
3
4
5
解答:
type First<T extends any[]> = T extends [] ? never : T[0]
获取元组长度
题目:对于给定的元组,您需要创建一个通用的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
2
3
4
5
解答:
type Length<T extends readonly any[]> = T['length']
Exclude
题目:实现内置的Exclude <T,U>
,从 T 中排除可分配给 U 的那些类型
type x = string | number | boolean
type y = string | number
type c = MyExclude<x, y>
const b: c = true
2
3
4
5
6
7
解答:
type MyExclude<T, U> = T extends U ? never : T
Awaited
题目:我们有 Promise<ExampleType>
如何获得 ExampleType
?
type b = Promise<string>
const c: Awaited<b> = ''
2
3
解答:
type MyAwaited<T extends Promise<any>> = T extends Promise<infer R> ? (R extends Promise<any> ? MyAwaited<R> : R) : never
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'
2
解答:
type If<C extends boolean, T, U> = C extends true ? T : U
Concat
题目:在类型系统中实现Array.concat
type Result = Concat<[1], [2]> // expected to be [1, 2]
解答:
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]
Includes
题目:在类型系统中实现Array.includes
type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false`
解答:
type Includes<T extends readonly any[], U> = T extends [infer F, ...infer R] ? (Equal<U, F> extends true ? true : Includes<R, 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 类型
const foo = (arg1: string, arg2: number): void => {}
type Result = MyParameters<typeof foo> // [string, number]
2
解答:
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer R) => any ? [...R] : never
Middle 题
获取函数返回类型
题目:不使用 ReturnType
实现 TypeScript 的 ReturnType<T>
范型。
const fn = (v: boolean) => {
if (v) return 1
else return 2
}
type a = MyReturnType<typeof fn> // 应推导出 "1 | 2"
2
3
4
5
6
解答:
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any
实现 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,
}
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]
}
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
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]
}
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`
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]>
}
2
3
元组转合集
题目:实现泛型TupleToUnion<T>
,它覆盖元组的值与其值联合。
type Arr = ['1', '2', '3']
const a: TupleToUnion<Arr> // expected to be '1' | '2' | '3'
2
3
解答:
type TupleToUnion<T extends any[]> = T[number]
可串联构造器
题目:在 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
}
}
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
}
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
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
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]
2
3
4
5
解答:
type Pop<T extends any[]> = T extends [...infer U, infer P] ? U : never
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)
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]
}>
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`
2
3
4
5
6
7
8
9
10
11
12
解答:
type LookUp<U, T extends string> = U extends { type: T } ? U : never
Trim Left
题目:删除字符串开头的空格
type trimed = TrimLeft<' Hello World '> // expected to be 'Hello World '
解答:
type TrimLeft<S extends string> = S extends `${' ' | '\n' | '\t'}${infer R}` ? TrimLeft<R> : S
Trim
题目:删除字符串开头和结尾的空格
type trimed = Trim<' Hello World '> // expected to be 'Hello World'
解答:
type space = ' ' | '\n' | '\t'
type Trim<S extends string> = S extends `${space}${infer R}` ? Trim<R> : S extends `${infer R}${space}` ? Trim<R> : S
2
Capitalize
题目:将第一个字符转为大写
type capitalized = Capitalize<'hello world'> // expected to be 'Hello world'
解答:
type MyCapitalize<S extends string> = S extends `${infer U}${infer R}` ? `${Uppercase<U>}${R}` : S
Replace
题目:替换给定的内容
type replaced = Replace<'types are fun!', 'fun', 'awesome'> // expected to be 'types are awesome!'
解答:
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
2
3
4
5
ReplaceAll
题目:替换全部给定的内容
type replaced = ReplaceAll<'t y p e s', ' ', ''> // expected to be 'types'
解答:
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
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
2
3
4
解答:
type AppendArgument<Fn, A> = Fn extends (...arg: infer P) => infer R ? (...arg: [...P, A]) => R : never
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']
解答:
type Permutation<T, U = T> = [T] extends [never] ? [] : T extends U ? [T, ...Permutation<Exclude<U, T>>] : []
Length of String
题目:计算字符串的长度
type a = 'hellow world'
type b = LengthOfString<a> // type b = 12
2
3
解答:
type LengthOfString<T extends string, U extends any[] = []> = T extends `${infer R}${infer K}` ? LengthOfString<K, [...U, R]> : U['length']
Flatten
题目:铺平数组
type flatten = Flatten<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, 5]
解答:
type Flatten<T extends any[]> = T extends [infer R, ...infer K]
? R extends any[]
? [...Flatten<R>, ...Flatten<K>]
: [R, ...Flatten<K>]
: T
2
3
4
5
Append to object
题目:拓展对象的属性
type Test = { id: '1' }
type Result = AppendToObject<Test, 'value', 4> // expected to be { id: '1', value: 4 }
2
解答:
type AppendToObject<T extends object, U extends string, V> = {
[P in keyof T | U]: P extends keyof T ? T[P] : V
}
2
3
Absolute
题目:获取数字的绝对值,返回绝对值的字符串形式
type Test = -100
type Result = Absolute<Test> // expected to be "100"
2
解答:
type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer R}` ? R : `${T}`
String to Union
题目:实现一个将接收到的 String 参数转换为一个字母 Union 的类型。
type Test = '123'
type Result = StringToUnion<Test> // expected to be "1" | "2" | "3"
2
解答:
type StringToUnion<T extends string> = T extends `${infer R}${infer U}` ? R | StringToUnion<U> : never
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 }
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
}
2
3
KebabCase
题目: FooBarBaz
-> foo-bar-baz
type a = 'forBarBaz'
type b = KebabCase<a> // for-bar-baz
2
3
解答:
type KebabCase<S> = S extends `${infer R}${infer U}` ? `${Lowercase<R>}${U extends Uncapitalize<U> ? KebabCase<U> : `-${KebabCase<U>}`}` : S
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 }
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
}
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.
2
解答:
type AnyOf<T extends readonly any[]> = T extends Array<0 | '' | false | [] | Record<PropertyKey, never>> ? false : true
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
2
3
4
5
解答:
type IsNever<T> = [T] extends [never] ? true : false
IsUnion
题目: 判断是否为联合类型
type case1 = IsUnion<string> // false
type case2 = IsUnion<string | number> // true
type case3 = IsUnion<[string | number]> // false
2
3
解答:
type IsUnion<T, K = T> = [T] extends [never] ? false : T extends K ? ([K] extends [T] ? false : true) : never
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
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
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", ""]
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>]
2
3
Drop Char
题目: 从字符串中剔除指定字符。
type Butterfly = DropChar<' b u t t e r f l y ! ', ' '> // 'butterfly!'
解答:
type DropChar<S, C> = S extends `${infer R}${infer U}` ? `${R extends C ? '' : R}${DropChar<U, C>}` : S
PickByType
题目:根据指定值筛选出符合的字段。
type OnlyBoolean = PickByType<
{
name: string
count: number
isReadonly: boolean
isEnable: boolean
},
boolean
> // { isReadonly: boolean; isEnable: boolean; }
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]
}
2
3
StartsWith
题目:实现StartsWith<T, U>
,接收两个 string 类型参数,然后判断T
是否以U
开头,根据结果返回true
或false
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
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
2
3
PartialByKeys
题目: 实现一个通用的PartialByKeys<T, K>
,它接收两个类型参数T
和K
。
K
指定应设置为可选的T
的属性集。当没有提供K
时,它就和普通的Partial<T>
一样使所有属性都是可选的。
interface User {
name: string
age: number
address: string
}
type UserPartialName = PartialByKeys<User, 'name'> // { name?:string; age:number; address:string }
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
2
3
4
5
RequiredByKeys
题目: 实现一个通用的RequiredByKeys<T, K>
,它接收两个类型参数T
和K
。
K
指定应设为必选的T
的属性集。当没有提供K
时,它就和普通的Required<T>
一样使所有的属性成为必选的。
interface User {
name?: string
age?: number
address?: string
}
type UserRequiredName = RequiredByKeys<User, 'name'> // { name: string; age?: number; address?: string }
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
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; }
2
3
4
5
6
7
解答:
type Mutable<T extends object> = {
-readonly [P in keyof T]: T[P]
}
2
3
OmitByType
题目: 根据指定类型排除属性
type OmitBoolean = OmitByType<
{
name: string
count: number
isReadonly: boolean
isEnable: boolean
},
boolean
> // { name: string; count: number }
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]
}
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];
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
2
3
Shift
题目: 实现Array.shift
type Result = Shift<[3, 2, 1]> // [2, 1]
解答:
type Shift<T extends any[]> = T extends [infer R, ...infer U] ? [...U] : never
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
2
3
解答:
type TupleToNestedObject<T extends any[], U> = T extends [infer R extends PropertyKey, ...infer K] ? { [key in R]: TupleToNestedObject<K, U> } : U
Reverse
题目: 实现Array.reverse
type a = Reverse<['a', 'b']> // ['b', 'a']
type b = Reverse<['a', 'b', 'c']> // ['c', 'b', 'a']
2
解答:
type Reverse<T extends any[]> = T extends [...infer R, infer U] ? [U, ...Reverse<R>] : T
Flip Arguments
题目: 返回一个反转了参数的函数类型
type Flipped = FlipArguments<(arg0: string, arg1: number, arg2: boolean) => void>
// (arg0: boolean, arg1: number, arg2: string) => void
2
解答:
type FlipArguments<T extends (...arg: any[]) => any> = T extends (...arg: infer R) => infer S ? (...arg: Reverse<R>) => S : T
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
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]>
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'
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]}`>}`
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'}
2
3
解答:
type Flip<T extends { [key: PropertyKey]: any }> = {
[P in keyof T as `${T[P]}`]: P
}
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']]
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>]
: []
: []
2
3
4
5
IsTuple
题目: 判断当前类型是否为元组
type case1 = IsTuple<[number]> // true
type case2 = IsTuple<readonly [number]> // true
type case3 = IsTuple<number[]> // false
2
3
解答:
type IsTuple<T> = [T] extends [never] ? false : T extends [any] | [] | readonly [any] ? true : false
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]]
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]
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 []
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>]
: []
2
3
4
5
Trunc
题目: 实现Math.Trunc
type A = Trunc<12.34> // 12
解答:
type Trunc<T extends string | number> = `${T}` extends `${infer U}.${infer R}` ? `${U}` : `${T}`
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
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
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'
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>}`
: ''
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
2
解答:
type LastIndexOf<T, U> = T extends [...infer R, infer P] ? (Equal<U, P> extends true ? R['length'] : LastIndexOf<R, U>) : -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]
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
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; }
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]
}
2
3
Construct Tuple
题目: 构造数组
type result = ConstructTuple<2> // expect to be [unknown, unkonwn]
解答:
type ConstructTuple<T extends number, U extends any[] = []> = T extends U['length'] ? U : ConstructTuple<T, [...U, unknown]>