Ts 工具类实现(持续更新中...)
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 property
todo.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]>; // 3
type head4 = First<[() => 123, { a: string }]>; // () => 123
type head5 = First<[]>; // never
type head6 = First<[null]>; // null
type 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 4
type spaceXLength = Length<typeof spaceX>; // expected 5

Exclude

type MyExclude<T, U> = T extends U ? never : T
MyExclude<'a' | 'b' | 'c', 'a'> // 'a' | 'b'
MyExclude<'a' | 'b' | 'c', 'a' | 'b'> // 'c'

MyAwaited

type MyAwaited<T> = T extends Promise<infer A> ? MyAwaited<A> : T
type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>
MyAwaited<X> // string
MyAwaited<Y> // { field: number }
MyAwaited<Z> // string | number

If

type If<C extends boolean, T, F> = C extends true ? T : F
If<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 : false
type Includes<T extends readonly any[], U> = T extends [infer P, ...infer R] ?
IsEqual<U, P> extends true ? true : Includes<R, U>
: false
Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'> // true
Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // false
Includes<[1, 2, 3, 5, 6, 7], 7> // true
Includes<[1, 2, 3, 5, 6, 7], 4> // false
Includes<[1, 2, 3], 2> // true
Includes<[1, 2, 3], 1> // true
Includes<[{}], { a: 'A' }> // false
Includes<[boolean, 2, 3, 5, 6, 7], false> // false
Includes<[true, 2, 3, 5, 6, 7], boolean> // false
Includes<[false, 2, 3, 5, 6, 7], false> // true
Includes<[{ a: 'A' }], { readonly a: 'A' }> // false
Includes<[{ readonly a: 'A' }], { a: 'A' }> // false
Includes<[1], 1 | 2> // false
Includes<[1 | 2], 1> // false
Includes<[null], undefined> // false
Includes<[undefined], null> // false

Push

type Push<T extends unknown[], U> = [...T, U]
type Push<T, U> = T extends [...infer P] ? [...P, U] : never
Push<[], 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] : never
Unshift<[], 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 : never
const fn = (v: boolean) => v ? 1 : 2
const fn1 = (v: boolean, w: any) => v ? 1 : 2
MyReturnType<() => string> // string
MyReturnType<() => 123>> // 12
MyReturnType<() => ComplexObject>> // ComplexObject
MyReturnType<() => Promise<boolean>> // Promise<boolean>
MyReturnType<() => () => 'foo'> // () => 'foo'
MyReturnType<typeof fn> // 1 | 2
MyReturnType<typeof fn1> // 1 | 2

Omit

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> = MyPick<T, MyExclude<keyof T, K>>
interface Todo {
title: string
description: string
completed: 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' | true
type b = TupleToUnion<[123]>; // 123

Chainable

基本思路:
  1. 返回自身.
  2. 使用option的时候会判断key是不是存在当前对象, 如果存在就赋值成never, 不存在则添加到对象上: K extends keyof O ? never: K.
  3. 添加的对象会进行类型合并: 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

  1. 通过展开运算符..., 获取最后一项.
  2. 通过数组 length, 读取最后一项.
  3. 通过递归, 每次排除第一项,读取最后一项.
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

  1. 使用extendsinfer来判断类型
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

  1. 使用模版字符串判断首位字符是不是' '\n\t.
  2. 使用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

  1. 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

  1. 使用extendsinfer来截取字符串的首个字符.
  2. 使用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

  1. 使用extendsinfer进行模版字符串的判断.
  2. 将字符串分成三份: A、From、B.
  3. 如果字符串满足${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

  1. 参考Replace实现.
  2. 添加一个递归处理.
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

  1. 使用extendsinfer推断出参数以及返回值.
  2. 使用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

  1. 使用递归将字符串转化为数组.
  2. 返回数组的长度.
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

  1. 使用递归的操作迭代每一个元素.
  2. 对每一个元素进行判断, 是否需要进一步扁平.
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

  1. 使用keyof T | U来进行循环.
  2. 使用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

  1. 使用递归的方式每次进行判断然后添加到结果字符串中.
  2. 使用模版字符串进行判断-${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

  1. 第一种: 将字符串转化为数组, 再通过T[number]转化.
  2. 第二种: 通过字符串模版、递归的方式每次提取一个字符串进行 union, 直到最后 union never.
  3. 第三种: 和第一种差不多, 区别在于这是每次递归的时候就使用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

  1. 找出两个对象的不同 key 数组(核心).
  2. 不同 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

  1. 使用Falsy来确定哪一些是假值.
  2. 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: string
flag: number
}
type NodeB = {
type: 'B'
id: number
flag: number
}
type NodeC = {
type: 'C'
name: string
flag: number
}
type ReplacedNodeA = {
type: 'A'
name: number
flag: string
}
type ReplacedNodeB = {
type: 'B'
id: number
flag: string
}
type ReplacedNodeC = {
type: 'C'
name: number
flag: string
}
type NoNameNodeA = {
type: 'A'
flag: number
name: never
}
type NoNameNodeC = {
type: 'C'
flag: number
name: never
}
type Nodes = NodeA | NodeB | NodeC
type ReplacedNodes = ReplacedNodeA | ReplacedNodeB | ReplacedNodeC
type NodesNoName = NoNameNodeA | NoNameNodeC | NodeB
type 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

  1. 匹配左边的符号位.
  2. 匹配中间的数据位.
  3. 匹配右边的%.
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-error
Expect<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-error
Mutable<'string'>,
// @ts-expect-error
Mutable<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

  1. 使用-?移除可选项属性.
  2. 使用RemoveUndefined移除undefined.
  3. 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

  1. 第一种是从前往后构建
  2. 第二种是从后往前构建
type TupleToNestedObject<T, U> = T extends [infer A extends PropertyKey, ...infer R] ? {
[P in A]: TupleToNestedObject<R, U>
}: U
type 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-error
FlipArguments<'string'>,
// @ts-expect-error
FlipArguments<{ key: 'value' }>,
// @ts-expect-error
FlipArguments<['apple', 'banana', 100, { a: 1 }]>,
// @ts-expect-error
FlipArguments<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. 消耗, 两个数字转化为数组, 每次减 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

  1. 将元素放到中间数组, 然后判断其内容是否等于指定长度, 如果等于指定长度就放到结果数组, 清空中间数组开始下一轮.
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'>>
];