https://:.zhihu.com/p/137745695
https://zhuanlan.zhihu.com/p/52662645
一、Easy
二、Medium
1.获取函数返回类型
不使用 ReturnType 实现 TypeScript 的 ReturnType
例如:
const fn = (v: boolean) => {
if (v)
return 1
else
return 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: string
description: string
completed: 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: 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
// 指定的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: 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<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'
实现
// 相当于遍历数组下标 number
type 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: T
type 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 : nerver
type 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:
S
type 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) => number
type 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']