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

  • 2022-05-19 上午 / itsuki0927 /
  • 0人浏览 • 0条评论 • 0人喜欢
最后更新于2022-07-02 上午
article-header-cover
简单? 不简单!

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'
    >
  >
];
永久地址: https://itsuki.cn/article/172
上一篇

博客船新版本来啦!

下一篇

如何使用Github Actions实现一个简单的ci/cd

你可能喜欢