https://:.zhihu.com/p/137745695
https://zhuanlan.zhihu.com/p/52662645
一、Easy
二、Medium
1.获取函数返回类型 
 
 
不使用 ReturnType 实现 TypeScript 的 ReturnType
例如:
const fn = (v: boolean) => {if (v)return 1elsereturn 2}type a = MyReturnType<typeof fn> // 应推导出 "1 | 2"
使用 infer获取返回值的类型
// 返回范型参数T的返回值类型type MyReturnType<T extends (args: any) => any > = T extends (...args: any) => infer R ? R : any
2.实现 Omit
不使用 Omit 实现 TypeScript 的 Omit
Omit 会创建一个省略 K 中字段的 T 对象。
例如:
interface Todo {title: stringdescription: stringcompleted: boolean}type TodoPreview = MyOmit<Todo, 'description' | 'title'>const todo: TodoPreview = {completed: false,}
//1、第一种 Exclude 获取 T 类型中有, K类型中没有的成员,Pick 返回 T类型中的存在的这几个成员type MyOmit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;//2、第二种type MyOmit<T, K extends keyof any> = {[key in Exclude<keyof T, K>]: T[key]}//3、第三种type MyOmit<T, K extends keyof any> = {[key in keyof T as key extends K ? never : key]: T[key]}
3.Readonly2
实现一个通用MyReadonly2
K指定应设置为Readonly的T的属性集。如果未提供K,则应使所有属性都变为只读,就像普通的Readonly
例如:
interface Todo {title: stringdescription: stringcompleted: boolean}const todo: MyReadonly2<Todo, 'title' | 'description'> = {title: "Hey",description: "foobar",completed: false,}todo.title = "Hello" // Error: cannot reassign a readonly propertytodo.description = "barFoo" // Error: cannot reassign a readonly propertytodo.completed = true // OK
// 指定的key加上readonly属性,其他的原样返回type MyReadonly2<T, K extends keyof T = keyof T> = {readonly [P in K]: T[P]} & {[S in Exclude<keyof T, K> : T[S]]}// K extends keyof T = keyof T 的理解:默认K 的值为泛型T的所有key,题目的要求是没有提供K的情况下全部设置readonly
4.深度Readonly
实现一个通用的DeepReadonly
您可以假设在此挑战中我们仅处理对象。数组,函数,类等都无需考虑。但是,您仍然可以通过覆盖尽可能多的不同案例来挑战自己。
例如:
type X = {x: {a: 1b: 'hi'}y: 'hey'}type Expected = {readonly x: {readonly a: 1readonly b: 'hi'}readonly y: 'hey'}type Todo = DeepReadonly<X> // should be same as `Expected`
实现如下:
type DeepReadonly<X>={readonly [P in keyof X]: T[P] extends object? T[P] extends Function? T[P]: DeepReadonly<T[P]>: T[P]}
5.元组转合集(TODO)
实现泛型TupleToUnion
例如:
type Arr = ['1', '2', '3']type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'
实现
// 相当于遍历数组下标 numbertype TupleToUnion<T extends any[]> = T[number]// 递归,符合数组格式,取出数组第一个和第二个组合成 First | Second,递归拼凑第一个到最后一个。 如果数组只有一个时,返回T[0]type TupleToUnion<T extends any[]> = T extends [infer First, infer Second, ...infer Rest] ? TupleToUnion<[First | Second, ...Rest]>
6.可串联构造器(TODO)
7.最后一个元素
实现一个通用Last
例如
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
实现
// ...展开运算符 获取除最后一个之前的元素, L 是最后一个type Last<T extends any[]> = T extends [...infer P, infer L] ? L : nerver
8.出堆
实现一个通用Pop
例如
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 P, infer L] ? P : nerver
9.Promise.all
键入函数PromiseAll,它接受PromiseLike对象数组,返回值应为Promise
const promise1 = Promise.resolve(3);const promise2 = 42;const promise3 = new Promise<string>((resolve, reject) => {setTimeout(resolve, 100, 'foo');});// expected to be `Promise<[number, 42, string]>`const p = Promise.all([promise1, promise2, promise3] as const)
实现
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]): Promise<{ [P in keyof T]: T[P] extends Promise<infer R> ? R : T[P] }>// 另一种实现方式type PromiseType<T> = T extends Promise<infer PT> ? PT: Ttype PromisesType<T extends readonly any[]> = Promise<T extends readonly [infer First, ...infer Rest]? [PromiseType<First>, ...PromisesType<Rest>]: []>/*type PromisesType<T extends readonly any[]> = Promise<{[P in keyof T]: PromiseType<T[P]>}>*/declare const PromiseAll: <T extends readonly any[]>(promises: T) => PromisesType<T>
10.Type Lookup
有时,您可能希望根据其属性在并集中查找类型。
在此挑战中,我们想通过在联合Cat | Dog中搜索公共type字段来获取相应的类型。换句话说,在以下示例中,我们期望LookUp
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<U extends {type: string}, K> = U extends { type: K } ? U : nervertype LookUp<U extends {type : string}, T extends U['type']> = U extends { type : T} ? U :never;
11.Trim Left
实现 TrimLeft
例如
type trimed = TrimLeft<' Hello World '> // 应推导出 'Hello World '
实现
type TrimLeft<T extends string> = T extends `${' ' | '\n' | '\t'}${infer R}` ? TrimLeft<R> : S
12.Trim Right
实现 TrimRight
例如
type Trimed = TrimRight<' Hello World '> // 应推导出 ' Hello World'
实现
type TrimRight<S extends string> = S extends `${infer L}${' ' | '\n' | '\t'}` ? TrimRight<L> : S
13.Trim
实现Trim
例如
type trimed = Trim<' Hello World '> // expected to be 'Hello World'
实现
type Trim<S extends string> = S extends`${' ' | '\n' | '\t'}${infer Rest}${' ' | '\n' | '\t'}` |`${' ' | '\n' | '\t'}${infer Rest}` |`${infer Rest}${' ' | '\n' | '\t'}` ? Trim<Rest> : S
14.Capitalize
实现 Capitalize
例如
type capitalized = Capitalize<'hello world'> // expected to be 'Hello world'
实现
// 解法一type Capitalize<S extends string> = S extends `${infer F}${infer R}` ? `${Uppercase<F>}${R}` : S// 解法二type Alphabet = {a: 'A'b: 'B'c: 'C'd: 'D'e: 'E'f: 'F'g: 'G'h: 'H'i: 'I'j: 'J'k: 'K'l: 'L'm: 'M'n: 'N'o: 'O'p: 'P'q: 'Q'r: 'R's: 'S't: 'T'u: 'U'v: 'V'w: 'W'x: 'X'y: 'Y'z: 'Z'}type Capitalize<S extends string> = S extends `${infer F}${infer R}` ?F extends keyof Alphabet ?`${Alphabet[F]}${R}` :S:Stype capitalized = Capitalize<'hello world'> // expected to be 'Hello world'
infer F 获取到了第一个字符,infer R 获取到的是除第一个元素之外的其他字符
15.Replace
实现 Replace 将字符串 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 Front}${infer From}${infer Behind}`? `${Front}${To}${Behind}` : S
16.ReplaceAll
17.追加参数
实现一个泛型 AppendArgument
type Fn = (a: number, b: string) => numbertype Result = AppendArgument<Fn, boolean>// 期望是 (a: number, b: string, x: boolean) => number
这道题的关键是拿到 Fn的参数和返回值类型,然后赋值给一个新的函数
实现
type AppendArgument<Fn, A> = Fn extends (...args: infer Args) => infer R ? (...args: [...Args, A]) => R : never
18.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']
// [T] extends [never] -> prevent it from being distributive// T extends keyof any -> declare T is either string, number, or symbol to traverse// [TT in T] -> 遍历 T// [TT, ...Permutation<Exclude<T, TT>>] -> Permutate the rest element// {}[T] -> 将对象变成数组的联合type Permutation<T extends keyof any> = [T] extends [never] ?[] :{[P in T]: [P, ...Permutation<Exclude<T, P>>]}[T]type perm = Permutation<'A' | 'B' | 'C'>
19.计算字符串的长度
计算字符串的长度,类似于 String#length 。
// 字符串模板中两个 infer 相邻,第一个 infer 只会推断出单个字符,这有助于一些递归操作,比如 ${infer L}${infer R} 去推断 abcd,L 会推断为 a,而 R 会推断为 bcd。type LengthOfString<S extends string, N extends any[] = []> = S extends `${infer L}${infer R}` ? LengthOfString<R,[...N, L]> : N['length']
