2022-05-19 发布921 次点击4 条评论需阅读80分钟
Ts 工具类实现(持续更新中...)
Pick
type MyPick<T, K extends keyof T> = {[P in K]: T[P];};interface Todo {title: string;description: string;completed: boolean;}type TodoPreview = MyPick<Todo, 'title' | 'completed'>;const todo: TodoPreview = {title: 'Clean room',completed: false,};
Readonly
type MyReadonly<T> = {readonly [K in keyof T]: T[K];};interface Todo {title: string;description: string;}const todo: MyReadonly<Todo> = {title: 'Hey',description: 'foobar',};todo.title = 'Hello'; // Error: cannot reassign a readonly propertytodo.description = 'barFoo'; // Error: cannot reassign a readonly property
Tuple
type TupleToObject<T extends readonly any[]> = {[K in T[number]]: K;};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'}
First of Array
type FirstObjProperty<T extends any[]> = T extends { 0: infer A } ? A : never;type FirstWithEmptyArray<T extends any[]> = T extends [] ? never : T[0];type First<T extends any[]> = T extends [infer First, ...infer _Rest]? First: never;type First<T extends any[]> = T[number] extends never ? never : T[0];type First<T extends any[]> = T extends never[] ? never : T[0];type head1 = First<['a', 'b', 'c']>; // 'a'type head3 = First<[3, 2, 1]>; // 3type head4 = First<[() => 123, { a: string }]>; // () => 123type head5 = First<[]>; // nevertype head6 = First<[null]>; // nulltype head7 = First<[undefined]>; // undefined
Length of Tuple
type Length<T extends any[]> = T['length'];const tesla = ['tesla', 'model 3', 'model X', 'model Y'] as const;const spaceX = ['FALCON 9','FALCON HEAVY','DRAGON','STARSHIP','HUMAN SPACEFLIGHT',] as const;type teslaLength = Length<typeof tesla>; // expected 4type spaceXLength = Length<typeof spaceX>; // expected 5
Exclude
type MyExclude<T, U> = T extends U ? never : TMyExclude<'a' | 'b' | 'c', 'a'> // 'a' | 'b'MyExclude<'a' | 'b' | 'c', 'a' | 'b'> // 'c'
MyAwaited
type MyAwaited<T> = T extends Promise<infer A> ? MyAwaited<A> : Ttype X = Promise<string>type Y = Promise<{ field: number }>type Z = Promise<Promise<string | number>>MyAwaited<X> // stringMyAwaited<Y> // { field: number }MyAwaited<Z> // string | number
If
type If<C extends boolean, T, F> = C extends true ? T : FIf<true, 'a', 'b'> // 'a'If<false, 'a', 2> // 2
Concat
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]Concat<[], []> // []Concat<[], [1]> // [1]Concat<[1, 2], [3, 4]> // [1, 2, 3, 4]Concat<['1', 2, '3'], [false, boolean, '4']> // ['1', 2, '3', false, boolean, '4']
Includes
type IsEqual<X, Y> = (<T>() => T extends X ? true : false) extends (<T>() => T extends Y ? true : false) ? true : falsetype Includes<T extends readonly any[], U> = T extends [infer P, ...infer R] ?IsEqual<U, P> extends true ? true : Includes<R, U>: falseIncludes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'> // trueIncludes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // falseIncludes<[1, 2, 3, 5, 6, 7], 7> // trueIncludes<[1, 2, 3, 5, 6, 7], 4> // falseIncludes<[1, 2, 3], 2> // trueIncludes<[1, 2, 3], 1> // trueIncludes<[{}], { a: 'A' }> // falseIncludes<[boolean, 2, 3, 5, 6, 7], false> // falseIncludes<[true, 2, 3, 5, 6, 7], boolean> // falseIncludes<[false, 2, 3, 5, 6, 7], false> // trueIncludes<[{ a: 'A' }], { readonly a: 'A' }> // falseIncludes<[{ readonly a: 'A' }], { a: 'A' }> // falseIncludes<[1], 1 | 2> // falseIncludes<[1 | 2], 1> // falseIncludes<[null], undefined> // falseIncludes<[undefined], null> // false
Push
type Push<T extends unknown[], U> = [...T, U]type Push<T, U> = T extends [...infer P] ? [...P, U] : neverPush<[], 1> // [1]Push<[1, 2], '3'> // [1, 2, '3']Push<['1', 2, '3'], boolean> // ['1', 2, '3', boolean]
Unshift
type Unshift<T extends unknown[], U> = [U, ...T]type Unshift<T, U> = T extends [...infer P] ? [U, ...P] : neverUnshift<[], 1> // [1]Unshift<[1, 2], 0> // [0, 1, 2 ]Unshift<['1', 2, '3'], boolean> // [boolean, '1', 2, '3']
MyParameters
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;const foo = (arg1: string, arg2: number): void => {};const bar = (arg1: boolean, arg2: { a: 'A' }): void => {};const baz = (): void => {};type cases = [Expect<Equal<MyParameters<typeof foo>, [string, number]>>,Expect<Equal<MyParameters<typeof bar>, [boolean, { a: 'A' }]>>,Expect<Equal<MyParameters<typeof baz>, []>>];
MyReturnType
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : neverconst fn = (v: boolean) => v ? 1 : 2const fn1 = (v: boolean, w: any) => v ? 1 : 2MyReturnType<() => string> // stringMyReturnType<() => 123>> // 12MyReturnType<() => ComplexObject>> // ComplexObjectMyReturnType<() => Promise<boolean>> // Promise<boolean>MyReturnType<() => () => 'foo'> // () => 'foo'MyReturnType<typeof fn> // 1 | 2MyReturnType<typeof fn1> // 1 | 2
Omit
type MyExclude<T, K> = T extends K ? never : Ttype MyPick<T, K extends keyof T> = {[P in K]: T[P]}type MyOmit<T, K> = MyPick<T, MyExclude<keyof T, K>>interface Todo {title: stringdescription: stringcompleted: boolean}MyOmit<Todo, 'description'> // { title: string; completed: string }MyOmit<Todo, 'description' | 'completed'> // { title: string }
MyReadonly2
type MyExclude<T, K> = T extends K ? never : T;type MyReadonly2<T, K extends keyof T = keyof T> = {readonly [P in K]: T[P];} & {[P in MyExclude<keyof T, K>]: T[P];};// 优化后type MyReadonly<T> = {readonly [K in keyof T]: T[K];};type MyExclude<T, K> = T extends K ? never : T;type MyPick<T, K extends keyof T> = {[P in K]: T[P];};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>;type A = MyReadonly2<Todo1>; // Readonly<Todo1>type B = MyReadonly2<Todo1, 'title' | 'description'>;// {// readonly title: string;// readonly description?: string | undefined;// completed: boolean;// }type C = MyReadonly2<Todo2, 'title' | 'description'>;// {// readonly title: string;// readonly description?: string | undefined;// completed: boolean;// }interface Todo1 {title: string;description?: string;completed: boolean;}interface Todo2 {readonly title: string;description?: string;completed: boolean;}interface Expected {readonly title: string;readonly description?: string;completed: boolean;}
DeepReadonly
type DeepReadonly2<T> = {readonly [K in keyof T]: keyof T[K] extends undefined? T[K]: DeepReadonly<T[K]>;};type DeepReadonly<T> = {readonly [K in keyof T]: T[K] extends Record<string, unknown> | Array<unknown>? DeepReadonly<T[K]>: T[K];};type X = {a: () => 22;b: string;c: {d: boolean;e: {g: {h: {i: true;j: 'string';};k: 'hello';};l: ['hi',{m: ['hey'];}];};};};type Expected = {readonly a: () => 22;readonly b: string;readonly c: {readonly d: boolean;readonly e: {readonly g: {readonly h: {readonly i: true;readonly j: 'string';};readonly k: 'hello';};readonly l: readonly ['hi',{readonly m: readonly ['hey'];}];};};};
TupleToUnion
type TupleToUnion<T extends any[]> = T[number];type TupleToUnion<T extends any[]> = T extends [infer First,infer Second,...infer Rest]? TupleToUnion<[First | Second, ...Rest]>: T[0];type a = TupleToUnion<[123, '456', true]>; // 123 | '456' | truetype b = TupleToUnion<[123]>; // 123
Chainable
基本思路:
- 返回自身.
- 使用
option
的时候会判断key
是不是存在当前对象, 如果存在就赋值成never
, 不存在则添加到对象上:K extends keyof O ? never: K
. - 添加的对象会进行类型合并:
Chainable<O & Record<K, V>>
.
type Chainable<O = {}> = {option<K extends string, V>(key: K extends keyof O ? never : K,value: V): Chainable<O & { [P in K]: V }>;get(): O;};
使用
Record
简化type Chainable<O = {}> = {option<K extends string, V>(key: K extends keyof O ? never : K,value: V): Chainable<O & Record<K, V>>;get(): O;};declare const a: Chainable;const result1 = a.option('foo', 123).option('bar', { value: 'Hello World' }).option('name', 'type-challenges').get();const result2 = a.option('name', 'another name')// @ts-expect-error.option('name', 'last name').get();type cases = [Expect<Alike<typeof result1, Expected1>>,Expect<Alike<typeof result2, Expected2>>];type Expected1 = {foo: number;bar: {value: string;};name: string;};type Expected2 = {name: string;};
Last
- 通过展开运算符
...
, 获取最后一项. - 通过数组 length, 读取最后一项.
- 通过递归, 每次排除第一项,读取最后一项.
type Last<T extends any[]> = T extends [...infer Rest, infer V] ? V : undefined;type Last<T extends any[]> = [undefined, ...T][T['length']];type Last<T extends any[]> = T extends [infer End]? End: T extends [infer First, ...infer Rest]? Last<Rest>: undefined;type Last<T extends any[]> = T extends [infer First, ...infer Rest]? T[Rest['length']]: undefined;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Last<[3, 2, 1]>, 1>>,Expect<Equal<Last<[() => 123, { a: string }]>, { a: string }>>];
Pop
type Pop<T extends unknown[]> = T extends [...infer R, infer L] ? R : [];
Shift
type Shift<T extends unknown[]> = T extends [infer L, ...infer R] ? L : [];
Unshift
type Unshift<T extends unknown[], V> = [V, ...T];
PromiseAll
- 使用
extends
、infer
来判断类型
declare function PromiseAll<T extends unknown[]>(values: readonly [...T]): Promise<{ [K in keyof T]: T[K] extends Promise<infer V> ? V : T[K] }>;import type { Equal, Expect } from '@type-challenges/utils';const promiseAllTest1 = PromiseAll([1, 2, 3] as const);const promiseAllTest2 = PromiseAll([1, 2, Promise.resolve(3)] as const);const promiseAllTest3 = PromiseAll([1, 2, Promise.resolve(3)]);type cases = [Expect<Equal<typeof promiseAllTest1, Promise<[1, 2, 3]>>>,Expect<Equal<typeof promiseAllTest2, Promise<[1, 2, number]>>>,Expect<Equal<typeof promiseAllTest3, Promise<[number, number, number]>>>];
LookUp
type LookUp<U, T> = U extends { type: T } ? U : never;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';interface Cat {type: 'cat';breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal';}interface Dog {type: 'dog';breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer';color: 'brown' | 'white' | 'black';}type Animal = Cat | Dog;type a = LookUp<Animal, 'dog'>;type cases = [Expect<Equal<LookUp<Animal, 'dog'>, Dog>>,Expect<Equal<LookUp<Animal, 'cat'>, Cat>>];
TrimLeft
- 使用模版字符串判断首位字符是不是
' '
、\n
、\t
. - 使用
infer Rest
提取剩下的字符进行递归处理.
type TrimLeft<S extends string> = S extends `${' ' | '\n' | '\t'}${infer Rest}`? TrimLeft<Rest>: S;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<TrimLeft<'str'>, 'str'>>,Expect<Equal<TrimLeft<' str'>, 'str'>>,Expect<Equal<TrimLeft<' str'>, 'str'>>,Expect<Equal<TrimLeft<' str '>, 'str '>>,Expect<Equal<TrimLeft<' \n\t foo bar '>, 'foo bar '>>,Expect<Equal<TrimLeft<''>, ''>>,Expect<Equal<TrimLeft<' \n\t'>, ''>>];
Trim
- 先
TrimLeft
左边空格、再TrimRight
右边空格.
type TrimLeft<S extends string> = S extends `${' ' | '\n' | '\t'}${infer R}`? TrimLeft<R>: S;type TrimRight<S extends string> = S extends `${infer R}${' ' | '\n' | '\t'}`? TrimRight<R>: S;type Trim<S extends string> = TrimLeft<TrimRight<S>>;type Trim<S extends string> = S extends| `${' ' | '\n' | '\t'}${infer R}`| `${infer R}${' ' | '\n' | '\t'}`? Trim<R>: S;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Trim<'str'>, 'str'>>,Expect<Equal<Trim<' str'>, 'str'>>,Expect<Equal<Trim<' str'>, 'str'>>,Expect<Equal<Trim<'str '>, 'str'>>,Expect<Equal<Trim<' str '>, 'str'>>,Expect<Equal<Trim<' \n\t foo bar \t'>, 'foo bar'>>,Expect<Equal<Trim<''>, ''>>,Expect<Equal<Trim<' \n\t '>, ''>>];
Capitalize
- 使用
extends
、infer
来截取字符串的首个字符. - 使用
UpperCase
范型来首字母大写.
type MyCapitalize<S extends string> = S extends `${infer F}${infer Rest}`? `${Uppercase<F>}${Rest}`: S;import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<MyCapitalize<'foobar'>, 'Foobar'>>,Expect<Equal<MyCapitalize<'FOOBAR'>, 'FOOBAR'>>,Expect<Equal<MyCapitalize<'foo bar'>, 'Foo bar'>>,Expect<Equal<MyCapitalize<''>, ''>>,Expect<Equal<MyCapitalize<'a'>, 'A'>>,Expect<Equal<MyCapitalize<'b'>, 'B'>>,Expect<Equal<MyCapitalize<'c'>, 'C'>>,Expect<Equal<MyCapitalize<'d'>, 'D'>>,Expect<Equal<MyCapitalize<'e'>, 'E'>>,Expect<Equal<MyCapitalize<'f'>, 'F'>>,Expect<Equal<MyCapitalize<'g'>, 'G'>>,Expect<Equal<MyCapitalize<'h'>, 'H'>>,Expect<Equal<MyCapitalize<'i'>, 'I'>>,Expect<Equal<MyCapitalize<'j'>, 'J'>>,Expect<Equal<MyCapitalize<'k'>, 'K'>>,Expect<Equal<MyCapitalize<'l'>, 'L'>>,Expect<Equal<MyCapitalize<'m'>, 'M'>>,Expect<Equal<MyCapitalize<'n'>, 'N'>>,Expect<Equal<MyCapitalize<'o'>, 'O'>>,Expect<Equal<MyCapitalize<'p'>, 'P'>>,Expect<Equal<MyCapitalize<'q'>, 'Q'>>,Expect<Equal<MyCapitalize<'r'>, 'R'>>,Expect<Equal<MyCapitalize<'s'>, 'S'>>,Expect<Equal<MyCapitalize<'t'>, 'T'>>,Expect<Equal<MyCapitalize<'u'>, 'U'>>,Expect<Equal<MyCapitalize<'v'>, 'V'>>,Expect<Equal<MyCapitalize<'w'>, 'W'>>,Expect<Equal<MyCapitalize<'x'>, 'X'>>,Expect<Equal<MyCapitalize<'y'>, 'Y'>>,Expect<Equal<MyCapitalize<'z'>, 'Z'>>];
Replace
- 使用
extends
、infer
进行模版字符串的判断. - 将字符串分成三份: A、From、B.
- 如果字符串满足
${A}${From}${B}
的话就替换成${A}${To}${B}
.
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;import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Replace<'foobar', 'bar', 'foo'>, 'foofoo'>>,Expect<Equal<Replace<'foobarbar', 'bar', 'foo'>, 'foofoobar'>>,Expect<Equal<ReplaceAll<'foobarbar', 'bar', 'foo'>, 'foofoofoo'>>,Expect<Equal<Replace<'foobarbar', '', 'foo'>, 'foobarbar'>>,Expect<Equal<Replace<'foobarbar', 'bar', ''>, 'foobar'>>,Expect<Equal<Replace<'foobarbar', 'bra', 'foo'>, 'foobarbar'>>,Expect<Equal<Replace<'', '', ''>, ''>>];
ReplaceAll
- 参考
Replace
实现. - 添加一个递归处理.
type ReplaceAll<S extends string,From extends string,To extends string> = From extends ''? S: S extends `${infer A}${From}${infer B}`? `${A}${To}${ReplaceAll<B, From, To>}`: S;type ReplaceAll<S extends string,From extends string,To extends string> = From extends ''? S: S extends `${infer A}${From}${infer B}`? `${ReplaceAll<A, From, To>}${To}${ReplaceAll<B, From, To>}`: S;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<ReplaceAll<'foobar', 'bar', 'foo'>, 'foofoo'>>,Expect<Equal<ReplaceAll<'foobar', 'bag', 'foo'>, 'foobar'>>,Expect<Equal<ReplaceAll<'foobarbar', 'bar', 'foo'>, 'foofoofoo'>>,Expect<Equal<ReplaceAll<'t y p e s', ' ', ''>, 'types'>>,Expect<Equal<ReplaceAll<'foobarbar', '', 'foo'>, 'foobarbar'>>,Expect<Equal<ReplaceAll<'barfoo', 'bar', 'foo'>, 'foofoo'>>,Expect<Equal<ReplaceAll<'foobarfoobar', 'ob', 'b'>, 'fobarfobar'>>,Expect<Equal<ReplaceAll<'foboorfoboar', 'bo', 'b'>, 'foborfobar'>>,Expect<Equal<ReplaceAll<'', '', ''>, ''>>];
AppendArgument
- 使用
extends
、infer
推断出参数以及返回值. - 使用
Pushed
工具函数添加参数.
type Pushed<T extends unknown[], R> = [...T, R];type AppendArgument<Fn, A> = Fn extends (...args: infer P) => infer R? (...params: Pushed<P, A>) => R: never;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type Case1 = AppendArgument<(a: number, b: string) => number, boolean>;type Result1 = (a: number, b: string, x: boolean) => number;type Case2 = AppendArgument<() => void, undefined>;type Result2 = (x: undefined) => void;type cases = [Expect<Equal<Case1, Result1>>, Expect<Equal<Case2, Result2>>];
LengthOfString
- 使用递归将字符串转化为数组.
- 返回数组的长度.
type LengthOfString<S extends string,T extends unknown[] = []> = S extends `${infer A}${infer R}`? LengthOfString<R, [...T, A]>: T['length'];/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<LengthOfString<''>, 0>>,Expect<Equal<LengthOfString<'kumiko'>, 6>>,Expect<Equal<LengthOfString<'reina'>, 5>>,Expect<Equal<LengthOfString<'Sound! Euphonium'>, 16>>];
Flatten
- 使用递归的操作迭代每一个元素.
- 对每一个元素进行判断, 是否需要进一步扁平.
type Flatten<T extends unknown[]> = T extends [infer First, ...infer Rest]? First extends unknown[]? [...Flatten<First>, ...Flatten<Rest>]: [First, ...Flatten<Rest>]: T;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Flatten<[]>, []>>,Expect<Equal<Flatten<[1, 2, 3, 4]>, [1, 2, 3, 4]>>,Expect<Equal<Flatten<[[1], [2]]>, [1, 2]>>,Expect<Equal<Flatten<[1, 2, [3, 4], [[[5]]]]>, [1, 2, 3, 4, 5]>>,Expect<Equal<Flatten<[{ foo: 'bar'; 2: 10 }, 'foobar']>,[{ foo: 'bar'; 2: 10 }, 'foobar']>>];
AppendToObject
- 使用
keyof T | U
来进行循环. - 使用
extends keyof T
判断是 T 的属性还是需要添加的属性.
type AppendToObject<T, U extends string | number | symbol, V> = {[P in keyof T | U]: P extends keyof T ? T[P] : V;};/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type test1 = {key: 'cat';value: 'green';};type testExpect1 = {key: 'cat';value: 'green';home: boolean;};type test2 = {key: 'dog' | undefined;value: 'white';sun: true;};type testExpect2 = {key: 'dog' | undefined;value: 'white';sun: true;home: 1;};type test3 = {key: 'cow';value: 'yellow';sun: false;};type testExpect3 = {key: 'cow';value: 'yellow';sun: false;isMotherRussia: false | undefined;};type cases = [Expect<Equal<AppendToObject<test1, 'home', boolean>, testExpect1>>,Expect<Equal<AppendToObject<test2, 'home', 1>, testExpect2>>,Expect<Equal<AppendToObject<test3, 'isMotherRussia', false | undefined>,testExpect3>>];
Absolute
- 使用递归的方式每次进行判断然后添加到结果字符串中.
- 使用模版字符串进行判断
-${infer V}
的方式进行判断和截取.
type Absolute<T extends number | string | bigint,L extends string = ''> = `${T}` extends `${infer A}${infer Rest}`? A extends '-'? Absolute<Rest, L>: Absolute<Rest, `${L}${A}`>: L;type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer V}`? V: `${T}`;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Absolute<0>, '0'>>,Expect<Equal<Absolute<-0>, '0'>>,Expect<Equal<Absolute<10>, '10'>>,Expect<Equal<Absolute<-5>, '5'>>,Expect<Equal<Absolute<'0'>, '0'>>,Expect<Equal<Absolute<'-0'>, '0'>>,Expect<Equal<Absolute<'10'>, '10'>>,Expect<Equal<Absolute<'-5'>, '5'>>,Expect<Equal<Absolute<-1_000_000n>, '1000000'>>,Expect<Equal<Absolute<9_999n>, '9999'>>];
StringToUnion
- 第一种: 将字符串转化为数组, 再通过
T[number]
转化. - 第二种: 通过字符串模版、递归的方式每次提取一个字符串进行 union, 直到最后 union never.
- 第三种: 和第一种差不多, 区别在于这是每次递归的时候就使用
T[number]
转化出来了, 在进行最终的合并, 而第一种是把所有的元素放到数组里, 最后在进行T[number]
.
type StringToArr<T extends string,L extends string[] = []> = T extends `${infer A}${infer R}` ? StringToArr<R, [...L, A]> : L;type StringToUnion<T extends string> = T extends ''? never: StringToArr<T>[number];type StringToUnion<T extends string,L = never> = T extends `${infer A}${infer B}` ? A | StringToUnion<B> : never;type StringToUnion<T extends string> = T extends `${infer L}${infer R}`? [L, StringToUnion<R>][number]: never;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<StringToUnion<''>, never>>,Expect<Equal<StringToUnion<'t'>, 't'>>,Expect<Equal<StringToUnion<'hello'>, 'h' | 'e' | 'l' | 'l' | 'o'>>,Expect<Equal<StringToUnion<'coronavirus'>,'c' | 'o' | 'r' | 'o' | 'n' | 'a' | 'v' | 'i' | 'r' | 'u' | 's'>>];
Diff
- 找出两个对象的不同 key 数组(核心).
- 不同 key 数组遍历组装成对象.
type Filter<A, B, K = A | B> = K extends A & B ? never : K;type Diff<O, O1> = {[P in Filter<keyof O, keyof O1>]: P extends keyof O? O[P]: P extends keyof O1? O1[P]: never;};type Diff<O, O1> = {[P in Exclude<keyof O | keyof O1, keyof O & keyof O1>]: P extends keyof O? O[P]: P extends keyof O1? O1[P]: never;};type Diff<O, O1> = {[P in keyof Omit<O, keyof O1> | keyof Omit<O1, keyof O>]: P extends keyof O? O[P]: P extends keyof O1? O1[P]: never;};/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type Foo = {name: string;age: string;};type Bar = {name: string;age: string;gender: number;};type Coo = {name: string;gender: number;};type cases = [Expect<Equal<Diff<Foo, Bar>, { gender: number }>>,Expect<Equal<Diff<Bar, Foo>, { gender: number }>>,Expect<Equal<Diff<Foo, Coo>, { age: string; gender: number }>>,Expect<Equal<Diff<Coo, Foo>, { age: string; gender: number }>>];
AnyOf
- 使用
Falsy
来确定哪一些是假值. Record<PropertyKey, never>
表示一个空对象.
type Falsy = 0 | '' | false | [] | Record<PropertyKey, never>;type AnyOf<T extends readonly any[]> = T extends Array<Falsy> ? false : true;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<AnyOf<[1, 'test', true, [1], { name: 'test' }, { 1: 'test' }]>, true>>,Expect<Equal<AnyOf<[1, '', false, [], {}]>, true>>,Expect<Equal<AnyOf<[0, 'test', false, [], {}]>, true>>,Expect<Equal<AnyOf<[0, '', true, [], {}]>, true>>,Expect<Equal<AnyOf<[0, '', false, [1], {}]>, true>>,Expect<Equal<AnyOf<[0, '', false, [], { name: 'test' }]>, true>>,Expect<Equal<AnyOf<[0, '', false, [], { 1: 'test' }]>, true>>,Expect<Equal<AnyOf<[0, '', false, [], { name: 'test' }, { 1: 'test' }]>, true>>,Expect<Equal<AnyOf<[0, '', false, [], {}]>, false>>,Expect<Equal<AnyOf<[]>, false>>];
ReplaceKeys
type ReplaceKeys<U, T, Y> = {[K in keyof U]: K extends T ? K extends keyof Y ? Y[K] : never : U[K]}/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils'type NodeA = {type: 'A'name: stringflag: number}type NodeB = {type: 'B'id: numberflag: number}type NodeC = {type: 'C'name: stringflag: number}type ReplacedNodeA = {type: 'A'name: numberflag: string}type ReplacedNodeB = {type: 'B'id: numberflag: string}type ReplacedNodeC = {type: 'C'name: numberflag: string}type NoNameNodeA = {type: 'A'flag: numbername: never}type NoNameNodeC = {type: 'C'flag: numbername: never}type Nodes = NodeA | NodeB | NodeCtype ReplacedNodes = ReplacedNodeA | ReplacedNodeB | ReplacedNodeCtype NodesNoName = NoNameNodeA | NoNameNodeC | NodeBtype cases = [Expect<Equal<ReplaceKeys<Nodes, 'name' | 'flag', { name: number; flag: string }>, ReplacedNodes>>,Expect<Equal<ReplaceKeys<Nodes, 'name', { aa: number }>, NodesNoName>>,]
IsNever
为什么不使用
T extends never
?T extends never
是分布式条件类型. 最后的结果条件类型分布在联合上. 如果 T 是一个联合 ExtendsNever 应用于联合的每个成员, 结果是所有应用程序的联合ExtendsNever<'a' | 'b'> == ExtendsNever<'a' > | ExtendsNever<'b'>
。never 是空集合. 它在 union 中的行为暗示了这一点 'a' | never == 'a'
. 因此, 在分配时 never, T extends never
永远不会应用, 因为此联合中没有成员, 因此结果是never
.type IsNever<T> = T[] extends never[] ? true : false;type IsNever<T> = [T] extends [never] ? true : false;type IsNever<T> = { K: T } extends { K: never } ? true : false;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<IsNever<never>, true>>,Expect<Equal<IsNever<never | string>, false>>,Expect<Equal<IsNever<''>, false>>,Expect<Equal<IsNever<undefined>, false>>,Expect<Equal<IsNever<null>, false>>,Expect<Equal<IsNever<[]>, false>>,Expect<Equal<IsNever<{}>, false>>];
RemoveIndexSignature
type RemoveIndexSignature<T> = {[P in keyof T as number extends P? never: string extends P? never: symbol extends P? never: P]: T[P];};/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type Foo = {[key: string]: any;foo(): void;};type Bar = {[key: number]: any;bar(): void;0: string;};const foobar = Symbol('foobar');type FooBar = {[key: symbol]: any;[foobar](): void;};type Baz = {bar(): void;baz: string;};type cases = [Expect<Equal<RemoveIndexSignature<Foo>, { foo(): void }>>,Expect<Equal<RemoveIndexSignature<Bar>, { bar(): void; 0: string }>>,Expect<Equal<RemoveIndexSignature<FooBar>, { [foobar](): void }>>,Expect<Equal<RemoveIndexSignature<Baz>, { bar(): void; baz: string }>>];
PercentageParser
- 匹配左边的符号位.
- 匹配中间的数据位.
- 匹配右边的%.
type GetNumber<T extends string> = T extends `+${infer R}`? ['+', R]: T extends `-${infer R}`? ['-', R]: ['', T];type PercentageParser<A extends string> = A extends `${infer R}%`? [...GetNumber<R>, '%']: [...GetNumber<A>, ''];type Parse1<A extends string> = A extends `${infer T}${infer _}`? T extends '+' | '-'? T: '': '';type Parse2<A extends string> = A extends `${Parse1<A>}${infer R}${Parse3<A>}`? R: '';type Parse3<A extends string> = A extends `${infer _}${'%'}` ? '%' : '';type PercentageParser<A extends string> = [Parse1<A>, Parse2<A>, Parse3<A>];type IsSign<T> = T extends '+' | '-' ? T : never;type GetNumber<T extends string> = T extends `${IsSign<infer R>}${infer L}`? [R, L]: ['', T];type PercentageParser<T extends string> = T extends `${infer R}%`? [...GetNumber<R>, '%']: [...GetNumber<T>, ''];/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type Case0 = ['', '', ''];type Case1 = ['+', '', ''];type Case2 = ['+', '1', ''];type Case3 = ['+', '100', ''];type Case4 = ['+', '100', '%'];type Case5 = ['', '100', '%'];type Case6 = ['-', '100', '%'];type Case7 = ['-', '100', ''];type Case8 = ['-', '1', ''];type Case9 = ['', '', '%'];type Case10 = ['', '1', ''];type Case11 = ['', '100', ''];type cases = [Expect<Equal<PercentageParser<''>, Case0>>,Expect<Equal<PercentageParser<'+'>, Case1>>,Expect<Equal<PercentageParser<'+1'>, Case2>>,Expect<Equal<PercentageParser<'+100'>, Case3>>,Expect<Equal<PercentageParser<'+100%'>, Case4>>,Expect<Equal<PercentageParser<'100%'>, Case5>>,Expect<Equal<PercentageParser<'-100%'>, Case6>>,Expect<Equal<PercentageParser<'-100'>, Case7>>,Expect<Equal<PercentageParser<'-1'>, Case8>>,Expect<Equal<PercentageParser<'%'>, Case9>>,Expect<Equal<PercentageParser<'1'>, Case10>>,Expect<Equal<PercentageParser<'100'>, Case11>>];
DropChar
// type DropChar<S extends string, C extends string> = C extends ''// ? S// : S extends `${infer A}${infer Rest}`// ? `${C extends A ? '' : A}${DropChar<Rest, C>}`// : S;// type DropChar<// S extends string,// C extends string// > = S extends `${infer A}${C}${infer B}` ? `${A}${DropChar<B, C>}` : S;type DropChar<S extends string,C extends string> = S extends `${infer A}${C}${infer B}` ? DropChar<`${A}${B}`, C> : S;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [// @ts-expect-errorExpect<Equal<DropChar<'butter fly!', ''>, 'butterfly!'>>,Expect<Equal<DropChar<'butter fly!', ' '>, 'butterfly!'>>,Expect<Equal<DropChar<'butter fly!', '!'>, 'butter fly'>>,Expect<Equal<DropChar<' butter fly! ', ' '>, 'butterfly!'>>,Expect<Equal<DropChar<' b u t t e r f l y ! ', ' '>, 'butterfly!'>>,Expect<Equal<DropChar<' b u t t e r f l y ! ', 'b'>, ' u t t e r f l y ! '>>,Expect<Equal<DropChar<' b u t t e r f l y ! ', 't'>, ' b u e r f l y ! '>>];
PickByType
type PickByType<T, U> = {[P in keyof T as T[P] extends U ? P : never]: T[P];};/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';interface Model {name: string;count: number;isReadonly: boolean;isEnable: boolean;}type cases = [Expect<Equal<PickByType<Model, boolean>,{ isReadonly: boolean; isEnable: boolean }>>,Expect<Equal<PickByType<Model, string>, { name: string }>>,Expect<Equal<PickByType<Model, number>, { count: number }>>];
StartsWith
type StartsWith<T extends string, U extends string> = T extends `${U}${infer A}`? true: false;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<StartsWith<'abc', 'ac'>, false>>,Expect<Equal<StartsWith<'abc', 'ab'>, true>>,Expect<Equal<StartsWith<'abc', 'abcd'>, false>>,Expect<Equal<StartsWith<'abc', ''>, true>>,Expect<Equal<StartsWith<'abc', ' '>, false>>];
EndsWith
type EndsWith<T extends string, U extends string> = T extends `${infer A}${U}`? true: false;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<EndsWith<'abc', 'bc'>, true>>,Expect<Equal<EndsWith<'abc', 'abc'>, true>>,Expect<Equal<EndsWith<'abc', 'd'>, false>>];
PartialByKeys
type Copy<T> = Pick<T, keyof T>;type Include<T, K> = T extends K ? T : never;type MyExclude<T, K> = T extends K ? never : T;type PartialByKeys<T, K = keyof T> = Copy<{[P in keyof T as Include<keyof T, P>]?: T[P];} & {[P in MyExclude<keyof T, K>]: T[P];}>;type PartialByKeys<T, K extends PropertyKey = keyof T> = Copy<Partial<T> & Omit<T, K>>;type PartialByKeys<T, K = keyof T> = Copy<Partial<Pick<T, K & keyof T>> & Omit<T, K & keyof T>>;
RequiredByKeys
type Copy<T> = Pick<T, keyof T>;type RequiredByKeys<T, K = keyof T> = Copy<Omit<T, K & keyof T> & Required<Pick<T, K & keyof T>>>;type RequiredByKeys<T, K = keyof T> = Copy<T & Required<Pick<T, K & keyof T>>>;
Mutable
type Mutable<T> = {-readonly [P in keyof T]: T[P];};/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';interface Todo1 {title: string;description: string;completed: boolean;meta: {author: string;};}type List = [1, 2, 3];type cases = [Expect<Equal<Mutable<Readonly<Todo1>>, Todo1>>,Expect<Equal<Mutable<Readonly<List>>, List>>];type errors = [// @ts-expect-errorMutable<'string'>,// @ts-expect-errorMutable<0>];
OmitByType
type OmitByType<T, U> = {[P in keyof T as T[P] extends U ? never : P]: T[P];};/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';interface Model {name: string;count: number;isReadonly: boolean;isEnable: boolean;}type cases = [Expect<Equal<OmitByType<Model, boolean>, { name: string; count: number }>>,Expect<Equal<OmitByType<Model, string>,{ count: number; isReadonly: boolean; isEnable: boolean }>>,Expect<Equal<OmitByType<Model, number>,{ name: string; isReadonly: boolean; isEnable: boolean }>>];
ModelEntries
- 使用
-?
移除可选项属性. - 使用
RemoveUndefined
移除undefined
. RemoveUndefined
使用[T] extends [undefined]
的原因: 如果碰到RemoveUndefined<string | undefined>
就会原样返回(分布式条件类型), 所以使用[]
来收敛(禁用掉分布式条件类型).
type RemoveUndefined<T> = [T] extends [undefined] ? T : Exclude<T, undefined>;type RemoveUndefined2<T> = [Exclude<T, undefined>] extends [never]? undefined: Exclude<T, undefined>;type ObjectEntries<T> = {[K in keyof T]-?: [K, RemoveUndefined<T[K]>];}[keyof T];/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';interface Model {name: string;age: number;locations: string[] | null;}type ModelEntries =| ['name', string]| ['age', number]| ['locations', string[] | null];type cases = [Expect<Equal<ObjectEntries<Model>, ModelEntries>>,Expect<Equal<ObjectEntries<Partial<Model>>, ModelEntries>>,Expect<Equal<ObjectEntries<{ key?: undefined }>, ['key', undefined]>>,Expect<Equal<ObjectEntries<{ key: undefined }>, ['key', undefined]>>];
TupleToNestedObject
- 第一种是从前往后构建
- 第二种是从后往前构建
type TupleToNestedObject<T, U> = T extends [infer A extends PropertyKey, ...infer R] ? {[P in A]: TupleToNestedObject<R, U>}: Utype TupleToNestedObject<T, U> = T extends [...infer A, infer B extends PropertyKey] ? TupleToNestedObject<A, {[P in B]: U}> : U/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils'type cases = [Expect<Equal<TupleToNestedObject<['a'], string>, { a: string }>>,Expect<Equal<TupleToNestedObject<['a', 'b'], number>, { a: { b: number } }>>,Expect<Equal<TupleToNestedObject<['a', 'b', 'c'], boolean>, { a: { b: { c: boolean } } }>>,Expect<Equal<TupleToNestedObject<[], boolean>, boolean>>,]
Reverse
type Reverse<T> = T extends [infer A, ...infer B] ? [...Reverse<B>, A] : T;type Reverse<T> = T extends [...infer A, infer B] ? [B, ...Reverse<A>] : T;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Reverse<[]>, []>>,Expect<Equal<Reverse<['a', 'b']>, ['b', 'a']>>,Expect<Equal<Reverse<['a', 'b', 'c']>, ['c', 'b', 'a']>>];
FlipArguments
type Reverse<T extends unknown[]> = T extends [...infer A, infer B]? [B, ...Reverse<A>]: T;type FlipArguments<T extends Function> = T extends (...args: infer A) => infer R? (...args: Reverse<A>) => R: never;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<FlipArguments<() => boolean>, () => boolean>>,Expect<Equal<FlipArguments<(foo: string) => number>, (foo: string) => number>>,Expect<Equal<FlipArguments<(arg0: string, arg1: number, arg2: boolean) => void>,(arg0: boolean, arg1: number, arg2: string) => void>>];type errors = [// @ts-expect-errorFlipArguments<'string'>,// @ts-expect-errorFlipArguments<{ key: 'value' }>,// @ts-expect-errorFlipArguments<['apple', 'banana', 100, { a: 1 }]>,// @ts-expect-errorFlipArguments<null | undefined>];
DeepFlatten
// 对 T 数组进行一次平铺type FlattenOnce<T extends any[]> = T extends [infer F, ...infer R]? [...(F extends any[] ? F : [F]), ...FlattenOnce<R>]: [];type IsFlatten<T extends any[]> = T extends [infer F, ...infer R]? F extends any[]? false: IsFlatten<R>: true;type FlattenDepth<T extends unknown[] = unknown[],D = 1,L extends unknown[] = []> = IsFlatten<T> extends true? T: L['length'] extends D? T: FlattenDepth<FlattenOnce<T>, D, [1, ...L]>;type FlattenDepth<T extends unknown[],D = 1,L extends unknown[] = []> = L['length'] extends D? T: T extends [infer A, ...infer B]? A extends any[]? [...FlattenDepth<A, D, [1, ...L]>, ...FlattenDepth<B, D, L>]: [A, ...FlattenDepth<B, D, L>]: T;type AddOn<T extends unknown[], O extends T[number] = unknown> = [...T, O];type Flatten<T extends unknown[]> = T extends [infer A, ...infer Rest]? A extends unknown[]? [...A, ...Flatten<Rest>]: [A, ...Flatten<Rest>]: [];type FlattenDepth<T extends unknown[],D = 1,L extends unknown[] = []> = L['length'] extends D ? T : FlattenDepth<Flatten<T>, D, AddOn<L>>;type A = Flatten<[1, 2, 3, [4, [5]]]>;type B = FlattenOnce<[1, 2, 3, [4, [5]]]>;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<FlattenDepth<[]>, []>>,Expect<Equal<FlattenDepth<[1, 2, 3, 4]>, [1, 2, 3, 4]>>,Expect<Equal<FlattenDepth<[1, [2]]>, [1, 2]>>,Expect<Equal<FlattenDepth<[1, 2, [3, 4], [[[5]]]], 2>, [1, 2, 3, 4, [5]]>>,Expect<Equal<FlattenDepth<[1, 2, [3, 4], [[[5]]]]>, [1, 2, 3, 4, [[5]]]>>,Expect<Equal<FlattenDepth<[1, [2, [3, [4, [5]]]]], 3>, [1, 2, 3, 4, [5]]>>,Expect<Equal<FlattenDepth<[1, [2, [3, [4, [5]]]]], 4>, [1, 2, 3, 4, 5]>>];
GreaterThan
- 消耗, 两个数字转化为数组, 每次减 1, 直到有一个数组为空即可判断结果.
type GreaterThan<T extends number,U extends number,Arr extends number[] = []> = Arr['length'] extends T? false: Arr['length'] extends U? true: GreaterThan<T, U, [1, ...Arr]>;type numToArr<T extends number, U extends number[] = []> = U['length'] extends T? U: numToArr<T, [1, ...U]>;type GreaterThan<T extends number,U extends number,S extends number[] = numToArr<T>,M extends number[] = numToArr<U>> = S['length'] extends 0? false: M extends [infer A, ...infer B]? GreaterThan<T, U, S extends [infer C, ...infer D] ? D : [], B>: true;type GreaterThan<T extends number,U extends number,S = numToArr<T>> = S extends [infer A, ...infer R]? R['length'] extends U? true: GreaterThan<T, U, R>: false;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<GreaterThan<1, 0>, true>>,Expect<Equal<GreaterThan<5, 4>, true>>,Expect<Equal<GreaterThan<4, 5>, false>>,Expect<Equal<GreaterThan<0, 0>, false>>,Expect<Equal<GreaterThan<20, 20>, false>>,Expect<Equal<GreaterThan<10, 100>, false>>,Expect<Equal<GreaterThan<111, 11>, true>>,Expect<Equal<GreaterThan<111, 112>, false>>];
Zip
type PlusOne<I extends number, T extends number[] = []> = T['length'] extends I? [1, ...T]['length']: AddOn<I, [1, ...T]>;type Min<A extends unknown[],B extends unknown[],I extends number[] = []> = I['length'] extends A['length']? A['length']: I['length'] extends B['length']? B['length']: Min<A, B, [1, ...I]>;type Zip<T1 extends unknown[],T2 extends unknown[],I extends number = 0,R extends unknown[] = [],MinL = Min<T1, T2>> = I extends MinL ? R : Zip<T1, T2, PlusOne<I>, [...R, [T1[I], T2[I]]]>;type Zip<T1 extends unknown[],T2 extends unknown[],K extends unknown[] = []> = T1['length'] extends K['length']? {[Key in keyof T1]: [T1[Key], Key extends keyof T2 ? T2[Key] : never];}: T2['length'] extends K['length']? {[Key in keyof T2]: [Key extends keyof T1 ? T1[Key] : never, T2[Key]];}: Zip<T1, T2, [1, ...K]>;type Slice<T extends unknown[],L extends number,R extends unknown[] = []> = R['length'] extends L? R: T extends [infer First, ...infer Rest]? Slice<Rest, L, [First, ...R]>: R;type UnsafeIndex<T extends unknown[], K> = T[K & keyof T];type Zip<T1 extends unknown[],T2 extends unknown[],SafeKeys = Slice<T1, T2['length']>> = {[K in keyof SafeKeys]: [UnsafeIndex<T1, K>, UnsafeIndex<T2, K>];};type Zip<T1 extends unknown[], T2 extends unknown[]> = T1 extends [infer F1,...infer R1]? T2 extends [infer F2, ...infer R2]? [[F1, F2], ...Zip<R1, R2>]: []: [];/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Zip<[], []>, []>>,Expect<Equal<Zip<[1, 2], [true, false]>, [[1, true], [2, false]]>>,Expect<Equal<Zip<[1, 2, 3], ['1', '2']>, [[1, '1'], [2, '2']]>>,Expect<Equal<Zip<[], [1, 2, 3]>, []>>,Expect<Equal<Zip<[[1, 2]], [3]>, [[[1, 2], 3]]>>];
IsTuple
type IsNever<T> = [T] extends [never] ? true : false;type IsTuple<T> = IsNever<T> extends true? false: T extends readonly [infer F, ...infer R] | []? true: false;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<IsTuple<[]>, true>>,Expect<Equal<IsTuple<[number]>, true>>,Expect<Equal<IsTuple<[number, string]>, true>>,Expect<Equal<IsTuple<readonly [1]>, true>>,Expect<Equal<IsTuple<{ length: 1 }>, false>>,Expect<Equal<IsTuple<number[]>, false>>,Expect<Equal<IsTuple<never>, false>>];
Chunk
- 将元素放到中间数组, 然后判断其内容是否等于指定长度, 如果等于指定长度就放到结果数组, 清空中间数组开始下一轮.
type Chunk<T extends unknown[],N extends number,M extends unknown[] = [],R extends unknown[] = []> = T extends [infer First, ...infer Rest]? M['length'] extends N? Chunk<Rest, N, [First], [...R, M]>: Chunk<Rest, N, [...M, First], R>: M extends []? []: [...R, M];type Chunk<T extends unknown[],N extends number,R extends unknown[] = []> = T extends [infer First, ...infer Rest]? R['length'] extends N? [R, ...Chunk<Rest, N, [First]>]: Chunk<Rest, N, [...R, First]>: R extends []? []: [R];/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Chunk<[], 1>, []>>,Expect<Equal<Chunk<[1, 2, 3], 1>, [[1], [2], [3]]>>,Expect<Equal<Chunk<[1, 2, 3], 2>, [[1, 2], [3]]>>,Expect<Equal<Chunk<[1, 2, 3, 4], 2>, [[1, 2], [3, 4]]>>,Expect<Equal<Chunk<[1, 2, 3, 4], 5>, [[1, 2, 3, 4]]>>,Expect<Equal<Chunk<[1, true, 2, false], 2>, [[1, true], [2, false]]>>];
Without
type ToUnion<T> = T extends number[] ? T[number] : T;type Without<T, U> = T extends [infer First, ...infer Rest]? First extends ToUnion<U>? Without<Rest, U>: [First, ...Without<Rest, U>]: T;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Without<[1, 2], 1>, [2]>>,Expect<Equal<Without<[1, 2, 4, 1, 5], [1, 2]>, [4, 5]>>,Expect<Equal<Without<[2, 3, 2, 3, 2, 3, 2, 3], [2, 3]>, []>>];
TrimRight
type TrimRight<S extends string> = S extends `${infer A}${' ' | '\n' | '\t'}`? TrimRight<A>: S;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<TrimRight<'str'>, 'str'>>,Expect<Equal<TrimRight<'str '>, 'str'>>,Expect<Equal<TrimRight<'str '>, 'str'>>,Expect<Equal<TrimRight<' str '>, ' str'>>,Expect<Equal<TrimRight<' foo bar \n\t '>, ' foo bar'>>,Expect<Equal<TrimRight<''>, ''>>,Expect<Equal<TrimRight<'\n\t '>, ''>>];
Fill
- 使用一个变量来维护是否填充, 如果开始填充就使用填充字符, 否则使用当前数组项.
type Fill<T extends unknown[],N,Start extends number = 0,End extends number = T['length'],Count extends unknown[] = [],Flag = Count['length'] extends Start ? true : false> = Count['length'] extends End? T: T extends [infer First, ...infer Rest]? Flag extends true? [N, ...Fill<T, N, Start, End, [...Count, 1], true>]: [First, ...Fill<T, N, Start, End, [...Count, 1]>]: T;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Fill<[], 0>, []>>,Expect<Equal<Fill<[], 0, 0, 3>, []>>,Expect<Equal<Fill<[1, 2, 3], 0, 0, 0>, [1, 2, 3]>>,Expect<Equal<Fill<[1, 2, 3], 0, 2, 2>, [1, 2, 3]>>,Expect<Equal<Fill<[1, 2, 3], 0>, [0, 0, 0]>>,Expect<Equal<Fill<[1, 2, 3], true>, [true, true, true]>>,Expect<Equal<Fill<[1, 2, 3], true, 0, 1>, [true, 2, 3]>>,Expect<Equal<Fill<[1, 2, 3], true, 1, 3>, [1, true, true]>>,Expect<Equal<Fill<[1, 2, 3], true, 10, 0>, [1, 2, 3]>>,Expect<Equal<Fill<[1, 2, 3], true, 0, 10>, [true, true, true]>>];
Trunc
type Trunc<N> = `${N}` extends `${infer A}${string}` ? A : `${N}`;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Trunc<0.1>, '0'>>,Expect<Equal<Trunc<1.234>, '1'>>,Expect<Equal<Trunc<12.345>, '12'>>,Expect<Equal<Trunc<-5.1>, '-5'>>,Expect<Equal<Trunc<'1.234'>, '1'>>,Expect<Equal<Trunc<'-10.234'>, '-10'>>,Expect<Equal<Trunc<10>, '10'>>];
IndexOf
type MyEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y? 1: 2? true: false;type IndexOf<T, U, Index extends unknown[] = []> = T extends [infer A,...infer R]? MyEqual<A, U> extends true? Index['length']: IndexOf<R, U, [...Index, 1]>: -1;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<IndexOf<[1, 2, 3], 2>, 1>>,Expect<Equal<IndexOf<[2, 6, 3, 8, 4, 1, 7, 3, 9], 3>, 2>>,Expect<Equal<IndexOf<[0, 0, 0], 2>, -1>>,Expect<Equal<IndexOf<[string, 1, number, 'a'], number>, 2>>,Expect<Equal<IndexOf<[string, 1, number, 'a', any], any>, 4>>];
Join
// type Join<T extends string[], U extends string | number> = T extends [infer First extends string, ...infer Rest extends string[]] ? Rest['length'] extends 0 ? First : `${First}${U}${Join<Rest, U>}` : ''type Join<T, U extends string | number, R extends string = ''> = T extends [infer F,...infer E]? Join<E, U, R extends '' ? F : `${R}${U}${F & string}`>: R;/* _____________ Test Cases _____________ */import type { Equal, Expect } from '@type-challenges/utils';type cases = [Expect<Equal<Join<['a', 'p', 'p', 'l', 'e'], '-'>, 'a-p-p-l-e'>>,Expect<Equal<Join<['Hello', 'World'], ' '>, 'Hello World'>>,Expect<Equal<Join<['2', '2', '2'], 1>, '21212'>>,Expect<Equal<Join<['o'], 'u'>, 'o'>>];