五块木头的logo
  • 技术的路
  • 生活的甜
  • 跑过的步
  • 与你一句
五块木头的logo五块木头
技术的路生活的甜跑过的步与你一句
湘ICP备2021020356号 / Copyright © 五块木头 Blog 2025
总浏览量 68.8万 😄😄😄最近访客来自 🇺🇸 Los Angeles, US
  • Pick
  • Readonly
  • Tuple
  • First of Array
  • Length of Tuple
  • Exclude
  • MyAwaited
  • If
  • Concat
  • Includes
  • Push
  • Unshift
  • MyParameters
  • MyReturnType
  • Omit
  • MyReadonly2
  • DeepReadonly
  • TupleToUnion
  • Chainable
  • Last
  • Pop
  • Shift
  • Unshift
  • PromiseAll
  • LookUp
  • TrimLeft
  • Trim
  • Capitalize
  • Replace
  • ReplaceAll
  • AppendArgument
  • LengthOfString
  • Flatten
  • AppendToObject
  • Absolute
  • StringToUnion
  • Diff
  • AnyOf
  • ReplaceKeys
  • IsNever
  • RemoveIndexSignature
  • PercentageParser
  • DropChar
  • PickByType
  • StartsWith
  • EndsWith
  • PartialByKeys
  • RequiredByKeys
  • Mutable
  • OmitByType
  • ModelEntries
  • TupleToNestedObject
  • Reverse
  • FlipArguments
  • DeepFlatten
  • GreaterThan
  • Zip
  • IsTuple
  • Chunk
  • Without
  • TrimRight
  • Fill
  • Trunc
  • IndexOf
  • Join
Ts 工具类实现(持续更新中...)
2022-05-19 发布921 次点击4 条评论需阅读80分钟
技术的路TypeScript

Ts 工具类实现(持续更新中...)

五块木头

文章首发于itsuki.cn个人博客

Pick

Readonly

Tuple

First of Array

Length of Tuple

Exclude

MyAwaited

If

Concat

Includes

Push

Unshift

MyParameters

MyReturnType

Omit

MyReadonly2

DeepReadonly

TupleToUnion

Chainable

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

Last

  1. 通过展开运算符..., 获取最后一项.
  2. 通过数组 length, 读取最后一项.
  3. 通过递归, 每次排除第一项,读取最后一项.

Pop

Shift

Unshift

PromiseAll

  1. 使用extends、infer来判断类型

LookUp

TrimLeft

  1. 使用模版字符串判断首位字符是不是' '、\n、\t.
  2. 使用infer Rest提取剩下的字符进行递归处理.

Trim

  1. 先TrimLeft左边空格、再TrimRight右边空格.

Capitalize

  1. 使用extends、infer来截取字符串的首个字符.
  2. 使用UpperCase范型来首字母大写.

Replace

  1. 使用extends、infer进行模版字符串的判断.
  2. 将字符串分成三份: A、From、B.
  3. 如果字符串满足${A}${From}${B}的话就替换成${A}${To}${B}.

ReplaceAll

  1. 参考Replace实现.
  2. 添加一个递归处理.

AppendArgument

  1. 使用extends、infer推断出参数以及返回值.
  2. 使用Pushed工具函数添加参数.

LengthOfString

  1. 使用递归将字符串转化为数组.
  2. 返回数组的长度.

Flatten

  1. 使用递归的操作迭代每一个元素.
  2. 对每一个元素进行判断, 是否需要进一步扁平.

AppendToObject

  1. 使用keyof T | U来进行循环.
  2. 使用extends keyof T判断是 T 的属性还是需要添加的属性.

Absolute

  1. 使用递归的方式每次进行判断然后添加到结果字符串中.
  2. 使用模版字符串进行判断-${infer V}的方式进行判断和截取.

StringToUnion

  1. 第一种: 将字符串转化为数组, 再通过T[number]转化.
  2. 第二种: 通过字符串模版、递归的方式每次提取一个字符串进行 union, 直到最后 union never.
  3. 第三种: 和第一种差不多, 区别在于这是每次递归的时候就使用T[number]转化出来了, 在进行最终的合并, 而第一种是把所有的元素放到数组里, 最后在进行T[number].

Diff

  1. 找出两个对象的不同 key 数组(核心).
  2. 不同 key 数组遍历组装成对象.

AnyOf

  1. 使用Falsy来确定哪一些是假值.
  2. Record<PropertyKey, never>表示一个空对象.

ReplaceKeys

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.

RemoveIndexSignature

PercentageParser

  1. 匹配左边的符号位.
  2. 匹配中间的数据位.
  3. 匹配右边的%.

DropChar

PickByType

StartsWith

EndsWith

PartialByKeys

RequiredByKeys

Mutable

OmitByType

ModelEntries

  1. 使用-?移除可选项属性.
  2. 使用RemoveUndefined移除undefined.
  3. RemoveUndefined 使用[T] extends [undefined]的原因: 如果碰到 RemoveUndefined<string | undefined>就会原样返回(分布式条件类型), 所以使用[]来收敛(禁用掉分布式条件类型).

TupleToNestedObject

  1. 第一种是从前往后构建
  2. 第二种是从后往前构建

Reverse

FlipArguments

DeepFlatten

GreaterThan

  1. 消耗, 两个数字转化为数组, 每次减 1, 直到有一个数组为空即可判断结果.

Zip

IsTuple

Chunk

  1. 将元素放到中间数组, 然后判断其内容是否等于指定长度, 如果等于指定长度就放到结果数组, 清空中间数组开始下一轮.

Without

TrimRight

Fill

  • 使用一个变量来维护是否填充, 如果开始填充就使用填充字符, 否则使用当前数组项.

Trunc

IndexOf

Join

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,
};
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
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'}
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
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
type MyExclude<T, U> = T extends U ? never : T
MyExclude<'a' | 'b' | 'c', 'a'> // 'a' | 'b'
MyExclude<'a' | 'b' | 'c', 'a' | 'b'> // 'c'
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
type If<C extends boolean, T, F> = C extends true ? T : F
If<true, 'a', 'b'> // 'a'
If<false, 'a', 2> // 2
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']
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
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]
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']
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>, []>>
];
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
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 }
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;
}
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'];
}
];
};
};
};
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
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;
};
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;
};
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 }>>
];
type Pop<T extends unknown[]> = T extends [...infer R, infer L] ? R : [];
type Shift<T extends unknown[]> = T extends [infer L, ...infer R] ? L : [];
type Unshift<T extends unknown[], V> = [V, ...T];
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]>>>
];
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>>
];
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'>, ''>>
];
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 '>, ''>>
];
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'>>
];
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<'', '', ''>, ''>>
];
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<'', '', ''>, ''>>
];
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>>];
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>>
];
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']
>
>
];
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
>
>
];
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'>>
];
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'
>
>
];
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 }>>
];
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>>
];
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>>,
]
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>>
];
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 }>>
];
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>>
];
// 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 ! '>>
];
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 }>>
];
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>>
];
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>>
];
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>
>;
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>>>;
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>
];
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 }
>
>
];
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]>>
];
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>>,
]
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']>>
];
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>
];
// 对 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]>>
];
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>>
];
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]]>>
];
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>>
];
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]]>>
];
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]>, []>>
];
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 '>, ''>>
];
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]>>
];
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'>>
];
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>>
];
// 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'>>
];