简单(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
,以及一个判断为假时的返回类型 F
。 C
只能是 true
或者 false
, T
和 F
可以是任意类型。
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
在类型系统里实现 JavaScript
的 Array.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
实现 TypeScript
的 ReturnType<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
实现 TypeScript
的 Omit<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>
,它带有两种类型的参数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
查看答案
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
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'>
获得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`
查看答案
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
,返回一个新的函数 G
。G
拥有 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]>;