type-challenges

1. Easy Part

1.1 Pick

主要实现是同 in 关键字将 联合类型的每个值取出。然后,通过 索引类型 访问该字段的类型,实现复制操作。

type MyPick<T extends Record<PropertyKey, any>, K extends keyof T> = {
  [P in K]: T[P];
} 
1.2 Readonly

同样利用 in 和 索引类型,为每个字段加上 readonly 修饰符

type MyReadonly<T extends Record<PropertyKey, any>> = {
  readonly [P in keyof T]: T[P];
};
1.3 Tuple to Object

推荐写法,通过 索引类型,直接取出所有的值。通过 PropertyKey 类型约束传入泛型值。

type TupleToObject<T extends readonly PropertyKey[]> = {
  [P in T[number]]: P;
};

我的写法比较复杂,我先通过条件语句,通过 infer 依次取出头一个元素,把剩余元素通过递归传入。

type TupleToObject<T extends readonly any[]> = T extends readonly [
  infer First,
  ...infer Rest
]
  ? Rest["length"] extends 0	// 判断 Rest 是否 长度为 0, 0 则不用递归
    ? First extends PropertyKey	// 判断 First 是否是一个合格的 键
      ? {
          [P in First]: First;
        }
      : never
    : First extends PropertyKey
    ? {
        [P in First]: First;
      } & TupleToObject<Rest> extends infer G // 将递归返回的对象合并并展开
      ? {
          [P in keyof G]: G[P];
        }
      : never
    : never
  : never;

1.4 First of Array

通过 条件类型 配合 infer 消费传入的泛型。

type First<T extends any[]> = T extends [infer R, ...infer Rest] ? R : never;
1.5 Length of Tuple

通过 索引类型,直接 索引 length 属性。返回值就是长度。

type Length<T extends readonly any[]> = T["length"];
1.6 Exclude

这里得利用联合类型在条件语句中的分布式特性,就比如说

  • 1 | 2 extends 1 | 2 
    // 等价于
    (1 extends 1) | (2 extends 1) | (1 extends 2) | (2 extends 2)
    

所以,我们当要排除选中的属性,只需要遇到选中属性时,让其返回 never 这样就排除了

type MyExclude<T extends keyof any | Function, K extends keyof any | Function> = T extends K
  ? never
  : T;
1.7 Awaited

条件语句 配合 infer,加上递归。

type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer Res>
  ? Res extends PromiseLike<any>
    ? MyAwaited<Res>
    : Res
  : never;
1.8 If

直接通过通过条件类型实现

type If<C, T, F> = C extends boolean ? (C extends true ? T : F) : never;
1.9 Concat

直接接收泛型,然后将其展开即可

type Concat<T extends any[], F extends any[]> = [...T, ...F];
1.10 Includes

通过 索引类型 和 条件类型 和 递归 实现

推荐代码

// 这里通过函数类型,来解决修饰符和联合类型分布式问题
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <U>() => U extends B
  ? 1
  : 2
  ? true
  : false;

type Includes<T extends any[], Q> = T extends [infer First, ...infer Rest]
  ? Equal<Q, First> extends true
    ? true
    : Rest["length"] extends 0
    ? false
    : Includes<Rest, Q>
  : false;

我们需要注意 联合类型和 any boolean, 因为 any 可以给任何类型赋值,也可以被任何类型赋值。boolean 相当于 true | false 这样的联合类型。

我的代码比较遗憾就当有修饰符的时候,会判断其相等。

type Includes<T extends any[], Q> = T extends [infer First, ...infer Rest]
  ? {xx: Q} extends {xx: First}
    ? {xx: First} extends {xx: Q}
      ? true
      : Rest["length"] extends 0
      ? false
      : Includes<Rest, Q>
    : Rest["length"] extends 0
    ? false
    : Includes<Rest, Q>
  : never;
1.11 Push

直接展开返回一个新的数组

type Push<T extends any[], E> = [...T, E];
1.12 Unshift

直接展开返回新的数组类型

type Unshift<T extends any[], E>= [E, ...T];
1.13 Parameters

通过 条件类型,加infer 实现。

type FunctionType = (...args: any[]) => any;
type MyParameters<T extends FunctionType> = T extends (
  ...arg: infer Params
) => any
  ? Params
  : null;

2. Medium Part

2.1 Get Return Type

infer 获取即可

type FunctionType = (...args: any[]) => any;
type MyReturnType<T extends FunctionType> = T extends (
  ...args: any[]
) => infer R
  ? R
  : never;

2.2 Omit

先取出所有键组成的联合类型,然后通过 Exclude 排除 需要排除的键。在通过映射类型实现。

type MyOmit<T extends Record<PropertyKey, any>, U> = {
  [P in Exclude<keyof T, U>]: T[P];
};
2.3 Readonly 2

其实可以分成两部分来做,需要添加修饰符的属性,和不需要添加修饰符的属性。

type MyReadonly2<
  T extends Record<PropertyKey, any>,
  K extends keyof T = keyof T
> = {
  [P in Exclude<keyof T, K>]: T[P];
} & {
  readonly [P in K]: T[P];
};

2.4 DeepReadonly

利用递归实现,判断当前是否是一个对象类型,是则递归。注意函数的处理

type DeepReadonly<T extends Record<PropertyKey, any>> = {
  readonly [P in keyof T]: T[P] extends Function 
    ? T[P]
    : DeepReadonly<T[P]>;
2.5 Tuple to Union
type TupleToUnion<T extends readonly any[]> = T[number];
2.6 Chainable Options
type Chainable<T = {}> = {
  option<U extends string, K>(
    key: U,
    value: K
  ): U extends keyof T
    ? Chainable<
        {
          [P in Exclude<keyof T, U>]: T[P];
        } & { [P in U]: K }
      >
    : Chainable<T & { [P in U]: K }>;
  get(): T;
};

// 简化写法,利用 never 属性被剔除的性质
type Chainable<T = {}> = {
  option<U extends string, K>(
    key: U,
    value: K
  ): Chainable<
    {
      [P in keyof T as P extends U ? never : P]: T[P];
    } & { [P in U]: K }
  >;
  get(): T;
};

这里用 T 保存传入的 key-value, 利用 ts 自动推断给泛型赋值类型值。最后,如果出现的重复的字段以重复的为准。

2.7 Last of Array
type Last<T extends any[]> = T["length"] extends 0
  ? never
  : T["length"] extends 1
  ? T[0]
  : T extends [...infer Rest, infer Goal]
  ? Goal
  : never;

先判断数组长度是否为 0, 然后判断数组长度是否为 1。 剩余情况都是大于1的。

2.8 Pop
type Pop<T extends any[]> = T["length"] extends 0
  ? []
  : T["length"] extends 1
  ? []
  : T extends [...infer Rest, infer Goal]
  ? Rest
  : never;

只是要注意数组长度的判断。

2.9 Promise.all
type PromiseFlat<T> = T extends Promise<infer R> ? PromiseFlat<R> : T;
declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<{[P in keyof T]: PromiseFlat<T[P]>}>  
2.10 Type Lookup

因为TS的类型判断是鸭子类型,所以我们可以基于该类型系统进行判断。

type LookUp<U, T> = U extends {type: T} ? U : never;
2.11 Trim Left

同过模板字符串类型,来实现

type TrimLeft<T extends string> = T extends `${" " | "\n" | "\t"}${infer Rest}`? TrimLeft<Rest> : T;
2.12 Trim

直接先,剔除左边空白字符,然后剔除右边空白字符。

type DeleteChar = " " | "\n" | "\t";
type TrimLeft<T extends string> = T extends `${DeleteChar}${infer Rest}`
  ? TrimLeft<Rest>
  : T;

type TrimRight<T extends string> = T extends `${infer Rest}${DeleteChar}`
  ? TrimRight<Rest>
  : T;

type Trim<T extends string> = TrimLeft<TrimRight<T>>;
2.13 Capitalize

利用TS 内置的Uppercase实现大写

type MyCapitalize<T extends string> = T extends `${infer F}${infer Rest}`? `${Uppercase<F>}${Rest}`: T; 
2.14 Replace

模板字符串类型实现

type Replace<S extends string, From extends string, To extends string> = From extends "" ? S : S extends `${infer Head}${From}${infer Tail}` ? `${Head}${To}${Tail}` : S;
2.15 ReplaceAll
type ReplaceAll<
  S extends string,
  From extends string,
  To extends string
> = From extends ""
  ? S
  : S extends `${infer Head}${From}${infer Tail}`
  ? `${Head}${To}${ReplaceAll<Tail, From, To>}`
  : S;

2.16 Append Argument
type FunctionTypes = (...args: any[]) => any;
type AppendArgument<Fn extends FunctionTypes, T> = Fn extends (
  ...args: infer Ps
) => infer R
  ? (...args: [...Ps, T]) => R
  : never;

2.17 Permutaion

利用联合类型会被展开的性质,实现排列

type Permutation<T, C = T> = [T] extends [never]
  ? []
  : T extends infer U // 这里就开始被展开了,C 用于保存未展开的类型
  ? [U, ...Permutation<Exclude<C, U>>]
  : [];
2.18 Length of String

通过数组求出

type LengthOfString<
  T extends string,
  Arr extends string[] = []
> = T extends `${infer Head}${infer Tail}`
  ? LengthOfString<Tail, [Head, ...Arr]>
  : Arr["length"];

用Split 工具函数分割,返回数组,然后取出数组的长度即可

type Split<
  T extends string,
  U extends string
> = T extends `${infer Head}${U}${infer Tail}`
  ? Head extends ""
    ? []
    : [Head, ...Split<Tail, U>]
  : T extends ""
  ? []
  : [T];

type LengthOfString<T extends string> = Split<T, "">["length"];

2.19 Flatten

利用递归实现,依次把每一层展开

type Flatten<T extends any[]> = T extends [infer Head, ...infer Rest]
  ? Head extends any[]
    ? [...Flatten<Head>, ...Flatten<Rest>]
    : [Head, ...Flatten<Rest>]
  : [];
2.20 Append to object

使用 & 级运算时,需要把他们展开放到一个对象中去

type FlattenObject<T extends {}> = {
  [P in keyof T]: T[P];
};
type AppendToObject<
  T extends {},
  Key extends PropertyKey,
  Value
> = FlattenObject<
  T & { [P2 in Key]: Value }
>;

// 简便写法
type AppendToObject<T extends {}, Key extends PropertyKey, Value> = {
  [P in keyof T | Key]: P extends keyof T ? T[P] : Value;
};
2.21 Absolute

把数字转换成字符串,在通过模板类型infer匹配

type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer N}`
  ? N
  : `${T}`;
2.22 String to Union
type StringToUnion<
  T extends string,
  Arr extends string[] = []
> = T extends `${infer Head}${infer Tail}`
  ? StringToUnion<Tail, [...Arr, Head]>
  : Arr[number];
2.23 Merge

大致可以分为三部分合并,T 独有的 + U 独有的 + T U 共有的。

type FlattenObject<T extends {}> = {
  [P in keyof T]: T[P];
};
type Merge<T extends {}, U extends {}> = FlattenObject<
  {
    [P in Exclude<keyof T, keyof U>]: T[P];
  } & {
    [P in Exclude<keyof U, keyof T>]: U[P];
  } & {
    [P in Extract<keyof U, keyof T>]: U[P];
  }
>;

// 简单写法
type Merge<T extends {}, U extends {}, O = T & U> = {[P in keyof O]: P extends keyof U ? U[P] : O[P]};
2.24 KebabCase

社区写法

type KebabCase<T extends string> = T extends `${infer U}${infer Rest}`
  ? Rest extends Uncapitalize<Rest>
    ? `${Uncapitalize<U>}${KebabCase<Rest>}`
    : `${Uncapitalize<U>}-${KebabCase<Rest>}`
  : T;
type Letters = ["a", "b", "c", "d", "e", "f", "g", "h", "I", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];

type ISLetter<C extends string> = C extends Letters[number] ? true : false;
type KebabCase<
  T extends string,
  S extends string = ""
> = T extends `${infer C}${infer Rest}`
  ? C extends "-" | "_" 
    ? KebabCase<Rest, `${S}${C}`>
    : ISLetter<C> extends true 
      ? C extends Uppercase<C>
        ? S extends ""
          ? KebabCase<Rest, Lowercase<C>>
          : KebabCase<Rest, `${S}-${Lowercase<C>}`>
      : KebabCase<Rest, `${S}${C}`>
    : KebabCase<Rest, `${S}${C}`>
  : S;
  • 我是先一个个取出判断,S用于保存结果。还得注意判断是不是字母。
2.25 Diff

其实就是把各自独有的属性合并成一个对象

type FlattenObject<T extends {}> = {
  [P in keyof T]: T[P];
};

type Diff<T extends {}, U extends {}> = FlattenObject<
  {
    [P in Exclude<keyof T, keyof U>]: T[P];
  } & {
    [P in Exclude<keyof U, keyof T>]: U[P];
  }
>;

// 简洁写法
type Diff<T extends {}, U extends {}> = {
  [P in Exclude<keyof T | keyof U, keyof T & keyof U>]: (T & U)[P];
}
2.26 AnyOf
type AnyFalsyValue = false | 0 | "" | undefined | null | [] | Record<PropertyKey, never>;

type AnyOf<T extends readonly any[]> = T extends [infer El, ...infer Other] ? El extends AnyFalsyValue ? AnyOf<Other> : true : false;

2.27 IsNever

never : 是Bottom type, 也就是说他是所有类型的子类型

type IsNever<T> = [T] extends [never] ? true : false; 
2.28 IsUnion
type IsUnion<T, C = T> = [T] extends [never]
  ? false
  : T extends C
  ? [C] extends [T]
    ? false
    : true
  : never;

利用泛型为联合类型的在约束中的分发性

(1 | 2 extends 1 | 2) => (1 extends 1 | 2) | (2 extends 1 | 2)

所以问题就转换成了,判断是否对联合类型进行了分发

2.29 ReplaceKeys

我们的利用泛型是联合类型被展开的特性,进行判断。同时如果 keys指定了,但是 Obj 中不存在该属性,则将该属性设置为 never

type ReplaceKeys<
  T,
  Keys,
  Obj extends Record<PropertyKey, any>,
  C = T
> = T extends C
  ? {
      [P in keyof T]: P extends Keys
        ? P extends keyof Obj
          ? Obj[P]
          : never
        : T[P];
    }
  : T;

2.30 Remove Index Signature

利用重映射和类型层级

type TypeLiteraOnly<T> = string extends T
  ? never
  : number extends T
  ? never
  : symbol extends T
  ? never
  : T;

type RemoveIndexSignature<T extends Record<PropertyKey, any>> = {
  [P in keyof T as TypeLiteraOnly<P>]: T[P];
};
2.31 Percentage Parser

先把第一个确定,数字把前后符号都去除就行。

type TrimLeftByMe<T extends string, O extends string> = T extends `${O}${infer Rest}` ? TrimLeftByMe<Rest, O>: T;
type TrimeRightByMe<T extends string, O extends string> = T extends `${infer Rest}${O}` ? TrimeRightByMe<Rest, O>: T;
type GetOp<T extends string> = T extends `${"-" | "+"}${infer Rest}` ? T extends `-${infer Rest}`? "-": "+" : "";
type GetP<T extends string> = T extends `${infer Rest}%` ? "%": "";
type PercentageParser<T extends string> = [GetOp<T>, TrimLeftByMe<TrimeRightByMe<T, "%">, "-" | "+">, GetP<T>];

简便写法

type PercentageParser<T extends string> = T extends `${infer F}${infer R}`
  ? F extends "-" | "+"
    ? [F, ...(R extends `${infer X}%` ? [X, "%"] : [R, ""])]
    : ["", ...(T extends `${infer Y}%` ? [Y, "%"] : [T, ""])]
  : ["", "", ""];
2.32 Drop Char

我的写法就是先去除左右两边在去除中间的

type TrimLeftByChar<T extends string, O extends string> = T extends `${O}${infer Rest}` ? TrimLeftByChar<Rest, O>: T;
type TrimRightByChar<T extends string, O extends string> = T extends `${infer Rest}${O}` ? TrimRightByChar<Rest, O>: T;
type TrimMidByChar<T extends string, O extends string> = T extends `${infer Head}${O}${infer Tail}` ? TrimMidByChar<`${Head}${Tail}`, O> : T;
type TrimByChar<T extends string, O extends string> = TrimMidByChar<TrimRightByChar<TrimLeftByChar<T, O>, O>, O>;
type DropChar<T extends string, O extends string> = TrimByChar<T, O>;
2.33 MinusOne(?)

比较复杂:https://github.com/type-challenges/type-challenges/issues/21627

// 1. 先将字符串反转
type Reverse<
  S extends string,
  A extends string = ""
> = S extends `${infer F}${infer R}` ? Reverse<R, `${F}${A}`> : A;

// 2. 映射 -1 的数
type DigitMinusOne<D extends string> =
  "09876543210" extends `${string}${D}${infer R}${string}` ? R : never;

// 3. 映射 +1 的数
type DigitPlusOne<D extends string> =
  "01234567890" extends `${string}${D}${infer R}${string}` ? R : never;

// 4. 翻转的数字-1
type RevMinusOne<T extends string> = T extends `${infer F}${infer N}${infer R}`
  ? // if `T` is a multi-digit number like '01', '55' or '123'
    F extends "0"
    ? `9${RevMinusOne<`${N}${R}`>}`
    : `${DigitMinusOne<F>}${N}${R}`
  : // if `T` is a single-digit number like '0', '1' or '8'
  T extends "0"
  ? "1-"
  : T extends "1"
  ? ""
  : DigitMinusOne<T>;
// 5. 翻转的数字+1
type RevPlusOne<T extends string> = T extends `${infer F}${infer N}${infer R}`
  ? // if `T` is a multi-digit number like '01', '55' or '123'
    F extends "9"
    ? `0${RevPlusOne<`${N}${R}`>}`
    : `${DigitPlusOne<F>}${N}${R}`
  : // if `T` is a single-digit number like '0', '1' or '8'
  T extends "9"
  ? "01"
  : DigitPlusOne<T>;

type MinusOne<T extends number> = `${T}` extends `-${infer AbsT}`
  ? // if `T` is negative - add one to the absolute value of the number
    // and then add back the '-' sign
    `-${Reverse<
      RevPlusOne<Reverse<AbsT>>
    >}` extends `${infer Res extends number}`
    ? Res
    : never
  : // if `T` is positive - same as before
  Reverse<RevMinusOne<Reverse<`${T}`>>> extends `${infer Res extends number}`
  ? Res
  : 0;

2.34 PickByType

利用重映射

type PickByType<T extends Record<PropertyKey, any>, Type> = {
  [P in keyof T as T[P] extends Type ? P : never]: Type;
};
2.35 StartsWith
type StartsWith<T extends string, P extends string> = T extends `${P}${string}` ? true : false;
2.36 EndsWith
type Reverse<
  S extends string,
  A extends string = ""
> = S extends `${infer F}${infer R}` ? Reverse<R, `${F}${A}`> : A;
type EndsWith<T extends string, P extends string> = Reverse<T> extends `${Reverse<P>}${string}` ? true : false;

2.37 PartialByKeys

遍历即可

type FlattenObject<T extends {}> = {
  [P in keyof T]: T[P];
};
type PartialByKeys<T extends Record<PropertyKey, any>, Keys extends keyof T = keyof T> = FlattenObject<{
  [P in Exclude<keyof T, Keys>]: T[P];
} & {
  [P in Keys]?: T[P];
}>;

2.38 RequiredByKeys

我的写法就是把,对象分为两部分

  • 通过传入泛型设置必选的属性
  • 不属于传入的泛型中的属性
type Flatten<T = {}> = {
  [P in keyof T]: T[P];
};
type RequiredByKeys<T extends {}, K extends keyof T = keyof T> = Flatten<{
  [P in keyof T as P extends K ? P: never]-?: T[P];
} & {
  [P in keyof T as P extends K ? never: P]: T[P]
}>
    
type Flatten<T = {}> = {
  [P in keyof T]: T[P];
};
type RequiredByKeys<T extends Record<PropertyKey, any>, K extends keyof T = keyof T> =  Flatten<T & {
  [P in K]-?:T[P];
}>

2.39 Mutable
type Mutable<T extends Record<PropertyKey, any>> = {
  -readonly [P in keyof T]: T[P];
}
2.40 OmitByType
type OmitByType<T extends Record<PropertyKey, any>, Type> = {
  [P in keyof T as Type extends T[P] ? never : P]: T[P];
};

2.41 ObjectEntries
type ObjectEntries<
  T extends Record<PropertyKey, any>,
  C = keyof T
> = C extends keyof T ? [C, T[C] extends undefined? undefined: Exclude<T[C], undefined>] : never;
// 另一种写法
type ObjectEntries<
  T extends Record<PropertyKey, any>,
  C = keyof T
> = C extends keyof T ? [C, T[C] extends undefined | infer Type ? Type : undefined] : never;
2.42 Shift
type Shift<T extends any[]> = T extends [infer F, ...infer R] ? R : [];
2.43 Tuple to Nested Object
type TupleToNestedObject<T extends string[], U> = T extends [
  infer F extends string,
  ...infer R extends string[]
]
  ? R["length"] extends 0
    ? { [P in F]: U }
    : {
        [P in F]: 
          TupleToNestedObject<R, U>
          
      }
  : U;
2.44 Reverse
type Reverse<T extends any[]> = T extends [infer F, ...infer Rest] ? [...Reverse<Rest>, F] : [];
2.45 Flip Arguments
type MyReverse<T extends any[]> = T extends [infer F, ...infer Rest]
  ? [...MyReverse<Rest>, F]
  : [];

type FunctionType = (...args: any[]) => any;
type FlipArguments<T extends FunctionType> = T extends (
  ...args: infer Args
) => infer R
  ? (...args: MyReverse<Args>) => R
  : never;
2.46 FlattenDepth

我们 利用Flatten展开一层, 在利用数组的对展开层数进行统计

type Flatten<T extends any[]> = T extends [infer Head, ...infer Rest]
  ? Head extends any[]
    ? [...Head, ...Flatten<Rest>]
    : [Head, ...Flatten<Rest>]
  : [];

type FlattenDepth<
  T extends any[],
  D extends number = 1,
  C extends unknown[] = [] // 用于统计层数
> = T extends Flatten<T> 	// 判断是否已经全部展开
  ? T
  : D extends C["length"]	// 判断是否已经达到指定展开层数
  ? T
  : FlattenDepth<Flatten<T>, D, [unknown, ...C]>;	// C 数组添加新的元素统计。
2.47 BEM style string

依次判断即可,利用联合类型在模板字符串类型中的分发性

type BEM<
  B extends string,
  E extends string[],
  M extends string[]
> = E["length"] extends 0
  ? M["length"] extends 0
    ? B
    : `${B}--${M[number]}`
  : M["length"] extends 0
  ? `${B}__${E[number]}`
  : `${B}__${E[number]}--${M[number]}`;
2.48 inorderTraversal

按照根节点的处理顺序递归即可

interface TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
}
type InorderTraversal<T extends TreeNode | null> = [T] extends [TreeNode]
  ? [
      ...InorderTraversal<T["left"]>,
      T["val"],
      ...InorderTraversal<T["right"]>
    ]
  : [];
2.49 Flip
type Flip<T extends Record<PropertyKey, any>> = {
  [P in keyof T as T[P] extends PropertyKey ? T[P] : `${T[P]}`]: P;
};
2.50 Fibonacci Sequence
// An = An-1 + An-2;
// A3 = A2 + A1
type Fibonacci<
  T extends number,
  A1 extends unknown[] = [unknown],
  A2 extends unknown[] = [unknown],
  D extends unknown[] = [unknown, unknown]
> = T extends 1 | 2
  ? 1
  : D["length"] extends T ? A2["length"] : Fibonacci<T, A2, [...A1, ...A2], [unknown, ...D]>;
2.51 AllCombinations(?)

先把每个字符拆成单独的一个,然后利用对象类型递归,我们每次先确定当前的第一个字符应该填什么,填完过后直接将使用的字符排除然后递归。

type StrToUnion<
  T extends string,
  S extends string = ""
> = T extends `${infer F}${infer R}` ? StrToUnion<R, F | S> : S; // 将 字符串拆分成联合类型

type AllCombinations<T extends string, U extends string = StrToUnion<T>> = [
  U
] extends [never]
  ? ""
  :
      | ""
      | {
          [P in U]: `${P}${AllCombinations<never, Exclude<U, P>>}`;
        }[U];
2.52 Greater Than

用数组做统计,先处理数值相等的情况,处理完过后,判断谁先到达统计的值。

type GreaterThan<
  D1 extends number,
  D2 extends number,
  A1 extends unknown[] = [],
  A2 extends unknown[] = []
> = D1 extends D2
  ? false
  : A1["length"] extends D1
  ? false
  : A2["length"] extends D2
  ? true
  : GreaterThan<D1, D2, [unknown, ...A1], [unknown, ...A2]>;
// 优化用一个数组类型即可
  
type GreaterThan<
  D1 extends number,
  D2 extends number,
  A extends unknown[] = []
> = D1 extends D2
  ? false
  : A["length"] extends D1
  ? false
  : A["length"] extends D2
  ? true
  : GreaterThan<D1, D2, [unknown, ...A]>;
2.53 Zip

先处理 T U 其中一个为 0 的情况,直接返回数组。

然后都不为零,这开始压缩到一起,答案数组 A 的长度其实就是下一个待压缩的下标

type Zip<
  T extends readonly any[],
  U extends readonly any[],
  A extends any[] = []
> = T["length"] extends 0
  ? []
  : U["length"] extends 0
  ? []
  : T["length"] extends A["length"]
  ? A
  : U["length"] extends A["length"]
  ? A
  : Zip<T, U, [...A, [T[A["length"]], U[A["length"]]]]>;
2.54 IsTuple

首先我们需要知道元组和数组的区别,元组有固定的长度,而数组长度属性是返回 number 类型。所以,我们可以通过判断length来判断

type IsTuple<T> = [T] extends [never]
  ? false
  : T extends readonly any[]
  ? number extends T["length"]
    ? false
    : true
  : false;
2.55 Chunk

依次取出每个数,用 Cur 存入,当 Cur 的长度 等于 分割长度,则将其加入到 A 数组中,并初始化为空数组。

type Chunk<
  T extends any[],
  N extends number,
  A extends any[] = [],
  Cur extends any[] = []
> = T extends [infer F, ...infer R]
  ? [F, ...Cur]["length"] extends N
    ? Chunk<R, N, [...A, [...Cur, F]], []>
    : Chunk<R, N, A, [...Cur, F]>
  : Cur["length"] extends 0
  ? A
  : [...A, Cur];
2.56 Fill

首先我们得利用之前写的 GreaterThan 来比较数字大小。然后,通过判断当前元素的下标,是不是在 [Start, End) 这个范围内。

type GreaterThan<
  D1 extends number,
  D2 extends number,
  hasEqueal extends boolean = false,
  A extends unknown[] = []
> = D1 extends D2
  ? hasEqueal
  : A["length"] extends D1
  ? false
  : A["length"] extends D2
  ? true
  : GreaterThan<D1, D2, hasEqueal, [unknown, ...A]>;
type Fill<
  T extends any[],
  N,
  Start extends number = 0,
  End extends number = T["length"],
  Index extends number[] = []
> = GreaterThan<Start, End> extends true
  ? T
  : T extends [infer F, ...infer R]
  ? GreaterThan<Index["length"], Start, true> extends true
    ? GreaterThan<Index["length"], End, true> extends false
      ? [N, ...Fill<R, N, Start, End, [1, ...Index]>]
      : [F, ...R]
    : [F, ...Fill<R, N, Start, End, [1, ...Index]>]
  : [];

2.57 Trim Right

注意空白字符的个数

type TrimRight<S extends string> = S extends `${infer R}${" " | "\n" | "\t"}` ? TrimRight<R> : S; 
2.58 Without

依次取出数组元素判断即可

type Without<T extends number[], U extends number[] | number> = T extends [
  infer F,
  ...infer R extends number[]
]
  ? U extends number[]
    ? F extends U[number]
      ? [...Without<R, U>]
      : [F, ...Without<R, U>]
    : F extends U
    ? [...Without<R, U>]
    : [F, ...Without<R, U>]
  : [];
2.59 Trunc

直接用模板字符串类型匹配点符号前面的数字即可

type Trunc<D extends number | string> =
  `${D}` extends `${infer F}.${string}` ? F : `${D}`;
2.60 IndexOf

通过下标依次访问即可,注意防止类型层级造成影响

type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <U>() => U extends B
  ? 1
  : 2
  ? true
  : false;
type IndexOf<T extends any[], U, Index extends number[] = []> = Equal<
  T[Index["length"]],
  U
> extends true
  ? Index["length"]
  : T["length"] extends Index["length"]
  ? -1
  : IndexOf<T, U, [1, ...Index]>;
2.61 Join

用一个数组类型的长度做下标索引,注意递归边界判断

type Join<
  T extends string[],
  U extends string | number,
  Index extends number[] = []
> = Index["length"] extends T["length"]
  ? ""
  : Index["length"] extends 0
  ? `${T[Index["length"]]}${Join<T, U, [1, ...Index]>}`
  : `${U}${T[Index["length"]]}${Join<T, U, [1, ...Index]>}`;
2.62 LastIndexOf

注意用一个类型来更新的答案

type LastIndexOf<
  T extends any[],
  Q,
  Index extends number[] = [],
  Res extends number = -1
> = Index["length"] extends T["length"]
  ? Res
  : Equal<T[Index["length"]], Q> extends true
  ? LastIndexOf<T, Q, [1, ...Index], Index["length"]>
  : LastIndexOf<T, Q, [1, ...Index], Res>;
2.63 Unique

用一个数组类型标记以下当前元素前面是否出现过即可

type Includes<T extends any[], Q> = T extends [infer F, ...infer R]
  ? Equal<F, Q> extends true
    ? true
    : Includes<R, Q>
  : false;
type Unique<T extends any[], Flag extends any[] = []> = T extends [
  infer F,
  ...infer R
]
  ? Includes<Flag, F> extends true
    ? [...Unique<R, Flag>]
    : [F, ...Unique<R, [F, ...Flag]>]
  : [];
2.64 MapTypes

我们必选注意联合类型的分布式特性。我们大致需要如下几个功能

  • 获取映射表中所有 mapForm 的类型组成的联合类型,注意联合类型特性
  • 通过类型从 映射表中 取出 对应的 mapTo 类型。
type MapEntire = {
  mapFrom: any;
  mapTo: any;
};
// 把所有的MapFrom 展开成联合类型
type MapTableFromToUnion<T extends MapEntire> = T extends MapEntire
  ? [T["mapFrom"]]
  : never;

type GetMapToByMapForm<
  MapTable extends MapEntire,
  Q
> = MapTable extends MapEntire
  ? Equal<MapTable["mapFrom"], Q> extends true
    ? MapTable["mapTo"]
    : never
  : never;
type MapTypes<
  T extends Record<PropertyKey, any>,
  MapTable extends MapEntire,
  U = MapTableFromToUnion<MapTable>
> = {
  [P in keyof T]: [T[P]] extends U ? GetMapToByMapForm<MapTable, T[P]> : T[P];
};
2.65 Construct Tuple
type ConstructTuple<
  D extends number,
  Res extends unknown[] = []
> = Res["length"] extends D ? Res : ConstructTuple<D, [unknown, ...Res]>;
2.66 Number Range

主要有以下功能

  • 构建一个 Start 大小的元组用于计算是否超出范围了。
  • 然后依次递归 递增即可
type ConstructTuple<
  D extends number,
  Res extends unknown[] = []
> = Res["length"] extends D ? Res : ConstructTuple<D, [unknown, ...Res]>;

type NumberRange<
  Start extends number,
  End extends number,
  Count extends unknown[] = ConstructTuple<Start>,
  Res extends number[] = []
> = Count["length"] extends End ? [...Res, End][number] : NumberRange<Start, End, [unknown, ...Count], [...Res, Count["length"]]>;
2.67 Combination

将数组转化成联合类型,然后库利用联合类型的分布式特性。

type Combination<T extends string[], U = T[number], K = U> = U extends string
  ? U | `${U} ${Combination<[], Exclude<K, U>>}`
  : "";
2.68 Subsequence

因为要保证原来的顺序,所以我们并不需要利用联合类型。而是直接判断每次当前枚举的元素在列表里或不在列表里即可。

type Subsequence<T extends any[]> = T extends [infer F, ...infer R] ? [F, ...Subsequence<R>] | [...Subsequence<R>] : [];
2.69 FirstUniqueCharIndex (?)

推荐写法: https://github.com/type-challenges/type-challenges/issues/21821

type FirstUniqueCharIndex<
  Post extends string,
  Pre extends string = "",
  I extends 0[] = []
> = Post extends `${infer F}${infer R}`
  ? `${Pre}${R}` extends `${string}${F}${string}`
    ? FirstUniqueCharIndex<R, `${Pre}${F}`, [...I, 0]>
    : I["length"]
  : -1;
  • 上面字节统计前后两部分,然后判断是否能够在除当前字符外能否找到其他和当前字符相同的字符即可。

我的思路是这样的

  • 先将字符串转化成数组
  • 我们以每个字符为分割点,将字符串分为两段。然后分别判断前一段是否包含该字符,后一段是否包含该字符。
  • 都不包含则返回下标即可
type GreaterThan<
  D1 extends number,
  D2 extends number,
  hasEqual extends boolean = false,
  A extends unknown[] = []
> = D1 extends D2
  ? hasEqual
  : A["length"] extends D1
  ? false
  : A["length"] extends D2
  ? true
  : GreaterThan<D1, D2, hasEqual, [unknown, ...A]>;

type Slice<
  T extends string,
  Start extends number,
  End extends number,
  Index extends number[] = []
> = T extends `${infer F}${infer R}`
  ? GreaterThan<Index["length"], Start, true> extends true
    ? GreaterThan<Index["length"], End, true> extends false
      ? `${F}${Slice<R, Start, End, [1, ...Index]>}`
      : ""
    : `${Slice<R, Start, End, [1, ...Index]>}`
  : "";

// 判断字符串中是否存在某个字符出
type HasChar<
  T extends string,
  Q extends string
> = T extends `${string}${Q}${string}` ? true : false;

type StringToArr<
  T extends string,
  Res extends string[] = []
> = T extends `${infer F}${infer R}` ? StringToArr<R, [...Res, F]> : Res;

type FirstUniqueCharIndex<
  T extends string,
  Index extends number[] = [],
  U extends string[] = StringToArr<T>
> = Index["length"] extends U["length"]
  ? -1
  : HasChar<Slice<T, 0, Index["length"]>, U[Index["length"]]> extends false
  ? HasChar<
      Slice<
        T,
        [1, ...Index]["length"] extends number ? [1, ...Index]["length"] : 0,
        U["length"]
      >,
      U[Index["length"]]
    > extends false
    ? Index["length"]
    : FirstUniqueCharIndex<T, [1, ...Index], U>
  : FirstUniqueCharIndex<T, [1, ...Index], U>;
2.70 GetMiddleElement

社区写法:https://github.com/type-challenges/type-challenges/issues/21800

type GetMiddleElement<T extends unknown[]> = T["length"] extends 0 | 1 | 2
  ? T
  : T extends [infer S, ...infer M, infer E]
  ? GetMiddleElement<M>
  : never;
  • 同时两边向里慢慢收缩,最后数组长度 0 1 2 必然是中间的数

我的思路是通过关系运算。

  • 数组的长度 必然是 2x 或 2x + 1, 所以通过这个关系,我们每次统一 x + x 是否 等于 数组长度。如果等于了则找到中间数了
type GetMiddleElement<
  T extends any[],
  I extends 0[] = []
> = T["length"] extends 0
  ? []
  : [...I, ...I, 0]["length"] extends T["length"]
  ? [T[I["length"]]]
  : [...I, ...I, 0, 0]["length"] extends T["length"]
  ? [
      T[I["length"]],
      T[[0, ...I]["length"] extends number ? [0, ...I]["length"] : 0]
    ]
  : GetMiddleElement<T, [0, ...I]>;
2.71 Integer
type Integer<T extends number> = number extends T ? never : `${T}` extends `${string}.${string}` ? never : T;
2.72 ToPrimitive

先处理完所有原始属性值,再处理对象值

type ToPrimitive<T> = T extends string
  ? string
  : T extends number
  ? number
  : T extends boolean
  ? boolean
  : T extends bigint
  ? bigint
  : T extends symbol
  ? symbol
  : {
      [P in keyof T]: ToPrimitive<T[P]>;
    };

2.73 DeepMutable

注意子要是非原始类型的值除函数以外都得取消只读。

type DeepMutable<T extends object> = {
  -readonly [K in keyof T]: T[K] extends object
    ? T[K] extends Function
      ? T[K]
      : DeepMutable<T[K]>
    : T[K];
};
2.74 All

更简便写法

type All<T extends any[], U> = T extends [U, ...infer R]
  ? All<R, U>
  : T["length"] extends 0
  ? true
  : false;

利用联合类型相同类型子保留一个的性质判断。

type All<T extends any[], U extends any> = T["length"] extends 0
  ? true
  : T extends [infer F, ...infer R]
  ? [U | F] extends [U]
    ? All<R, U>
    : false
  : true;
2.75 Filter

注意使用数组阻断联合类型的分发特性

type Filter<
  T extends any[],
  U
> = T extends [infer F, ...infer R]
  ? [F] extends [U]
    ? [F, ...Filter<R, U>]
    : [...Filter<R, U>]
  : [];

3. Hard Part

3.1 Simple Vue

这个题目让我们必须知道 关于 This 的设定。默认情况下 this 是被隐式指定成 any 的。我们必须 将隐式指定关闭 noImplicitThis: true

  • 注意:this 的设置必须作为函数的第一值。
  • ThisType 工具,是用于修改对象中的 this 类型的。
  • OmitThisParameter(用于去除函数的this声明,返回的是函数类型)、ThisParameterType(返回的是this的声明)、ThisType(为对象设置this)
// 1. 定义三个属性的基础类型
type BaseComputed = Record<PropertyKey, Function>;
type BaseMethods = Record<PropertyKey, Function>;
type BaseData = Record<PropertyKey, unknown>;

// 2. 因为是通过Proxy代理实现的,所以我们并不用真正的拿到方法执行。而是直接获得返回值结果。在函数中我们就需要直接拿到返回结果的类型。
type ForComputed<T extends BaseComputed> = {
  [P in keyof T]: T[P] extends () => infer R ? R : never;
};

declare function SimpleVue<
  Data extends BaseData,
  Computed extends BaseComputed,
  Methods extends BaseMethods
>(options: {
  data(this: unknown): Data;	// data 函数中的 this 应该什么都没有
  computed: Computed & ThisType<Data & Computed>; // 为 this 设置类型为 Data 和 Computed
  methods: Methods & ThisType<Data & ForComputed<Computed> & Methods>; // 为 this 设置 Data  ForComputed<Computed> 和 Methods
}): Data & Computed & Methods;
3.2 Currying

题目说了,一个函数子接收一个参数。所以,我们只需要把参数列表拆成 单参数的函数,然后利用返回类型连接起来即可。

type CurryingType<T extends Function> = T extends (
  first: infer First,
  ...rest: infer Rest
) => infer R
  ? Rest["length"] extends 0
    ? T
    : (first: First) => CurryingType<(...args: Rest) => R>
  : never;

 
declare function Currying<T extends Function>(fn: T): CurryingType<T>;
3.3 Union to Intersection

利用extends对逆变位置会被推断成交集的特性

  • 协变:子类型可以赋值给父类型
  • 逆变:父类型不可以赋值给子类型
    • 而逆变是在函数参数中存在。对于函数的参数,并不能把父类型赋值给子类型。因为函数内部可能会调用子类型有的方法,而父类型没有的方法
type UnionToIntersection<U> = (U extends any ? (x: U) => any : never) extends (x: infer R) => any ? R : never; 
3.4 Get Required

先将类型全部变成必填属性,然后,判断每个键,变前和变后是否兼容。如果不兼容则说明,不满足协变,也就是说之前就是可选类型。兼容则说明,变前就是必填类型。

type AllRequired<T extends Record<PropertyKey, unknown>> = {
  [P in keyof T]-?: T[P];
};
type GetRequired<T extends Record<PropertyKey, unknown>> = {
  [P in keyof T as [T[P]] extends [AllRequired<T>[P]] ? P : never]: T[P];
};
3.5 Get Optional

同样先变成Required,然后再判断是否相等。相等就说明之前就是必填属性,直接返回 never。不等说明之前是可选属性,直接返回。

type GetOptional<T extends Record<PropertyKey, unknown>> = {
  [P in keyof T as T[P] extends Required<T>[P] ? never : P]: T[P];
};
3.6 Required Keys

直接先取出所有必填类型组成一个对象,然后用 keyof 返回键组成的联合类型

type RequiredKeys<T extends Record<PropertyKey, unknown>> = keyof {
  [P in keyof T as T[P] extends Required<T>[P] ? P : never]: P;
};
3.7 Optional Keys

同样,先取出所有可选类型,然后用 keyof 取出键组成的联合类型。

type OptionalKeys<T extends Record<PropertyKey, unknown>> = keyof {
  [P in keyof T as T[P] extends Required<T>[P] ? never : P]: P;
};

3.8 Capitalize Words

用一个类型来保存一个单词,一个类型来保存结果。然后判断遇到的是否是字母,如果不是字母说明前面保存的字母已经是一个单词了。直接首字母大写即可。非字母记得保留到结果字符中。

type Letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
type IsLetter<T extends string> = T extends Letters[number] ? true : false;
type CapitalizeWords<
  T extends string,
  Res extends string = "",
  Word extends string = ""
> = T extends `${infer F}${infer R}`
  ? IsLetter<F> extends false
    ? CapitalizeWords<R, `${Res}${Capitalize<Word>}${F}`, "">
    : CapitalizeWords<R, Res, `${Word}${F}`>
  : `${Res}${Capitalize<Word>}`;
// 另一种判断是否是字母的方法,如果传入的是非字母那大小写转换必然是相同的。			
type IsLetter<T extends string> = Uppercase<T> extends Lowercase<T> ? false : true;
3.9 CamelCase

传入的值大致可以分为有下滑线的情况和没有下滑线的情况。考虑这两种情况即可。

type IsSnakeCase<T extends string> = T extends `${string}_${string}`
  ? true
  : false;
type CamelCase<
  T extends string,
  Res extends string = ""
> = IsSnakeCase<T> extends true
  ? T extends `${infer F}_${infer R}`
    ? Res extends ""
      ? CamelCase<R, Lowercase<F>>
      : CamelCase<R, `${Res}${Capitalize<Lowercase<F>>}`>
    : `${Res}${Capitalize<Lowercase<T>>}`
  : Res extends ""
  ? Lowercase<T>
  : `${Res}${Capitalize<Lowercase<T>>}`;

// 更简洁的写法
// 直接获取一个单词和下一个单词的首字母,然后,将单词小写,获取的首字母大写即可。	
type CamelCase<T extends string> = T extends `${infer F}_${infer M}${infer R}`
  ? `${Lowercase<F>}${Uppercase<M>}${CamelCase<R>}`
  : Lowercase<T>;
3.10 C-printf Parser

直接匹配 % 的后一个字符,如果该字符满足条件这加入到答案中,然后再判断剩余字符串。

type ControlsMap = {
  c: 'char'
  s: 'string'
  d: 'dec'
  o: 'oct'
  h: 'hex'
  f: 'float'
  p: 'pointer'
}

type ParsePrintFormat<
  T extends string,
  Res extends string[] = []
> = T extends `${string}%${infer F}${infer R}`
  ? F extends keyof ControlsMap
    ? ParsePrintFormat<R, [...Res, ControlsMap[F]]>
    : ParsePrintFormat<R, Res>
  : Res;
3.11 Vue Basic Props

终点是 对 Props 的转换。

  • 当传入的是一个类时,默认传入的是构造函数的类型
  • 当传入的是一个实例,传入的是 new 一个构造函数的类型
  • 当传入数组时,我们可以获取其来联合类型分别进行上面的判断。
type BaseComputed = Record<PropertyKey, Function>;
type BaseMethods = Record<PropertyKey, Function>;
type BaseData = Record<PropertyKey, unknown>;
type BaseProps = Record<PropertyKey, unknown>;
type ForComputed<T extends BaseComputed> = {
  [P in keyof T]: T[P] extends () => infer R ? R : never;
};
type ForProps<T extends BaseProps> = {
  [P in keyof T]: T[P] extends (...args: any[]) => infer R
    ? R
    : T[P] extends { type: (...args: any[]) => infer R }
    ? R
    : T[P] extends { type: new (...args: any[]) => infer R }
    ? R
    : T[P] extends { type: (infer K)[] }
    ? K extends (...args: any[]) => infer R
      ? R
      : T[P]
    : Equal<{}, T[P]> extends true ? any : T[P];
};
declare function VueBasicProps<
  Props extends BaseProps,
  Data extends BaseData,
  Computed extends BaseComputed,
  Methods extends BaseMethods
>(options: {
  props: Props;
  data(this: ForProps<Props>): Data;
  methods: Methods &
    ThisType<ForProps<Props> & Data & ForComputed<Computed> & Methods>;
  computed: Computed & ThisType<ForProps<Props> & Data & Computed & Methods>;
}): Props & Data & Methods & Computed;
3.12 IsAny(?)
type MyEqual<T, U> = (<T>() => T extends U ? 1 : 2) extends <U>() => U extends T
  ? 1
  : 2
  ? true
  : false;
type IsAny<T> = MyEqual<T, any>;
3.13 Typed Get

每次先判断Target是否再当前对象类型中,如果不在则继续且还未匹配完则继续递归匹配。

type Get<
  T extends Record<string, unknown>,
  Target extends string
> = Target extends keyof T
  ? T[Target]
  : Target extends `${infer F}.${infer R}`
  ? T[F] extends Record<string, unknown>
    ? Get<T[F], R>
    : never
  : never;
3.14 String to Number

更简洁的写法:

type ToNumber<T extends string> = T extends `${infer F extends number}` ? F : never;

思路:

  • 依次把每一位转化成数字,然后把上一次的结果 乘10 再相加
type NumberMap = {
  "0": 0;
  "1": 1;
  "2": 2;
  "3": 3;
  "4": 4;
  "5": 5;
  "6": 6;
  "7": 7;
  "8": 8;
  "9": 9;
};
// 两数相加
type Add<
  A extends number,
  B extends number,
  CountA extends 0[] = [],
  CountB extends 0[] = []
> = CountA["length"] extends A
  ? CountB["length"] extends B
    ? [...CountA, ...CountB]["length"]
    : Add<A, B, CountA, [0, ...CountB]>
  : CountB["length"] extends B
  ? Add<A, B, [0, ...CountA], CountB>
  : Add<A, B, [0, ...CountA], [0, ...CountB]>;

// 两数相乘
// 2 x 5 = 2 + 2 + 2 + 2
type Mul<
  A extends number,
  B extends number,
  Res extends number = 0,
  Cnt extends 0[] = []
> = Cnt["length"] extends B ? Res : Mul<A, B, Add<A, Res>, [0, ...Cnt]>;

type ToNumber<
  T extends string,
  Res extends number = 0
> = T extends `${infer F}${infer R}`
  ? F extends keyof NumberMap
    ? ToNumber<
        R,
        NumberMap[F] extends 0 ? Mul<Res, 10> : Add<Mul<Res, 10>, NumberMap[F]>
      >
    : never
  : Res;

3.15 Tuple Filter
type FilterOut<T extends readonly any[], U> = T extends [infer F, ...infer R]
  ? [F] extends [U]
    ? [...FilterOut<R, U>]
    : [F, ...FilterOut<R, U>]
  : [];
3.16 Tulpe to Enum Object

用数组统计,依次递增即可啦。

type FlattenObject<T extends Record<PropertyKey, unknown>> = {
  [P in keyof T]: T[P];
}

type EnumNumber<T extends readonly string[], Index extends 0[] = []> = T extends readonly [infer F extends string, ...infer R extends readonly string[]] ? {
  readonly [P in F as Capitalize<P>]: Index["length"]
} & EnumNumber<R, [0, ...Index]> : {};
type Enum<
  T extends readonly string[],
  IsTrue extends boolean = false,
> = IsTrue extends false
  ? {
      readonly [P in T[number] as Capitalize<P>]: P;
    }
  : FlattenObject<EnumNumber<T>>
3.17 printf
type Format<T extends string> = T extends `${string}%${infer F}${infer R}`
  ? F extends "d" | "s"
    ? F extends "d"
      ? (d1: number) => Format<R>
      : (s1: string) => Format<R>
    : Format<R>
  : string;
3.18 Deep object to unique

使用 unique symbol 类型,来设置独一无二的键(这样避免设置字符串,导致键名重复)。然后,我们通过使用 父对象和属性 来作为该对象的唯一标记。那么,此时这个对象就是一个独一无二的对象了。

declare const sym: unique symbol;
type DeepObjectToUniq<T extends object> = {
  [P in keyof T]: T[P] extends object
    ? T[P] extends Function
      ? T[P]
      : DeepObjectToUniq<T[P]> & { readonly [sym]?: [T, P] }
    : T[P];
} & {
  readonly [sym]?: unknown;
};

3.19 Length to String 2

因为TS最多递归 45 层,题目要求处理长度上百的字符串。所以,每一层递归我们要尽可能处理多的字符。以二进制递减的顺序处理即可

type LengthOfString<T extends string, Res extends string[] = []> = 
    T extends `${infer A1}${infer A2}${infer A3}${infer A4}${infer A5}${infer A6}${infer A7}${infer A8}${infer A9}${infer B1}${infer B2}${infer B3}${infer B4}${infer B5}${infer B6}${infer B7}${infer B8}${infer B9}${infer C1}${infer C2}${infer C3}${infer C4}${infer C5}${infer C6}${infer C7}${infer C8}${infer C9}${infer D1}${infer D2}${infer D3}${infer D4}${infer D5}${infer D6}${infer D7}${infer D8}${infer D9}${infer E1}${infer E2}${infer E3}${infer E4}${infer E5}${infer E6}${infer E7}${infer E8}${infer E9}${infer F1}${infer F2}${infer F3}${infer F4}${infer F5}${infer F6}${infer F7}${infer F8}${infer F9}${infer R}` ? LengthOfString<R, [A1, A2, A3, A4, A5, A6, A7, A8, A9, B1, B2, B3, B4, B5, B6,B7, B8, B9, C1,C2,C3,C4,C5,C6,C7,C8,C9,D1,D2,D3,D4,D5,D6,D7,D8,D9,E1,E2,E3,E4,E5,E6,E7,E8,E9,F1,F2,F3,F4,F5,F6,F7,F8,F9, ...Res]> 
  : T extends `${infer A1}${infer A2}${infer A3}${infer A4}${infer A5}${infer A6}${infer A7}${infer A8}${infer A9}${infer B1}${infer B2}${infer B3}${infer B4}${infer B5}${infer B6}${infer B7}${infer B8}${infer B9}${infer C1}${infer C2}${infer C3}${infer C4}${infer C5}${infer C6}${infer C7}${infer C8}${infer C9}${infer D1}${infer D2}${infer R}` ? LengthOfString<R, [A1, A2, A3, A4, A5, A6, A7, A8, A9, B1, B2, B3, B4, B5, B6,B7, B8, B9, C1,C2,C3,C4,C5,C6,C7,C8,C9,D1,D2, ...Res]> 
  : T extends `${infer A1}${infer A2}${infer A3}${infer A4}${infer A5}${infer A6}${infer A7}${infer A8}${infer A9}${infer B1}${infer B2}${infer B3}${infer B4}${infer B5}${infer B6}${infer B7}${infer B8}${infer B9}${infer R}` ?  LengthOfString<R, [A1, A2, A3, A4, A5, A6, A7, A8, A9, B1, B2, B3, B4, B5, B6,B7, B8, B9, ...Res]> 
  : T extends `${infer A1}${infer A2}${infer A3}${infer A4}${infer A5}${infer A6}${infer A7}${infer A8}${infer A9}${infer R}` ? LengthOfString<R, [A1, A2, A3, A4, A5, A6, A7, A8, A9, ...Res]> 
  : T extends `${infer A1}${infer A2}${infer A3}${infer A4}${infer A5}${infer R}` ? LengthOfString<R, [A1, A2, A3, A4, A5, ...Res]> 
  : T extends `${infer A1}${infer A2}${infer R}` ? LengthOfString<R, [A1, A2, ...Res]> 
  : T extends `${infer A1}${infer R}` ? LengthOfString<R, [A1, ...Res]> : Res["length"];
3.20 Union To Tuple

这里我们必须要知道Ts中的一个概念,当函数的交叉类型执行 extends 并 infer R 获取函数的返回值。那么他将返回交叉类型最后一个元素。通过这个特性,我们可以将联合类型先转换成函数组成的联合类型。然后,再提取最后一个元素。循环往复,直到所有值都被提取即可。

  • 逆变(函数参数位置)位置的联合类型展开,结果是交叉类型哦
// 把联合类型转成函数组成的交叉类型,函数的返回值属性联合类型中的值
type UnionToInterSectionFn<T> = (
  T extends T ? (x: () => T) => unknown : never
) extends (x: infer R) => unknown
  ? R
  : never;

// 获取联合类型的最后一个元素
type GetUnionLast<T> = UnionToInterSectionFn<T> extends () => infer R
  ? R
  : never;
type UnionToTuple<T, Res extends unknown[] = [], Last = GetUnionLast<T>> = [
  T
] extends [never]
  ? Res
  : UnionToTuple<Exclude<T, Last>, [Last, ...Res]>;
3.21 String Join

注意通过泛型获取参数消费哦

type Join<
  T extends string[],
  C extends string,
  Res extends string = ""
> = T extends [infer F extends string, ...infer R extends string[]]
  ? Res extends ""
    ? Join<R, C, F>
    : Join<R, C, `${Res}${C}${F}`>
  : Res;
declare function join<C extends string>(
  delimiter: C
): <T extends string[]>(...parts: T) => Join<T, C>;
3.22 DeepPick

先利用联合类型展开运算。然后,再利用函数参数逆变推断生成交叉类型。

type GetObject<
  T extends Record<string, unknown>,
  Target extends string
> = Target extends keyof T
  ? {
      [P in Target]: T[Target];
    }
  : Target extends `${infer F}.${infer R}`
  ? T[F] extends Record<string, unknown>
    ? {
        [P in F]: GetObject<T[F], R>;
      }
    : never
  : never;


type DeepPickUnion<
  T extends Record<string, unknown>,
  U extends string
> = U extends string ? GetObject<T, U> : never;

type UnionToIntersection<T> = (T extends unknown ? (x: T) => unknown: never) extends (x: infer R) => any ? R : never;

type DeepPick<
  T extends Record<string, unknown>,
  U extends string
> = UnionToIntersection<DeepPickUnion<T, U>>;

3.23 Pinia

比较简单按要求来就行了

type BaseState = Record<PropertyKey, unknown>;
type BaseGetters = Record<PropertyKey, Function>;
type BaseAcions = Record<PropertyKey, Function>;
type ForGetters<T extends BaseGetters> = {
  [P in keyof T]: T[P] extends () => infer R ? R : never;
};
declare function defineStore<
  State extends BaseState,
  Getters extends BaseGetters,
  Actions extends BaseAcions
>(store: {
  readonly id: string;
  state(this: void): State;
  getters?: Getters & ThisType<Readonly<State & ForGetters<Getters>>>;
  actions?: Actions & ThisType<State & ForGetters<Readonly<Getters>> & Readonly<Actions>>;
}): State & ForGetters<Getters> & Readonly<Actions>;
3.24 Camelize

数组内的对象也需要转换成驼峰。

type CamelCase<T extends string> = T extends `${infer F}_${infer M}${infer R}`
  ? `${Lowercase<F>}${Uppercase<M>}${CamelCase<R>}`
  : Lowercase<T>;

type ArrToCamelize<T extends any[]> = T extends [infer F, ...infer R]
  ? F extends Record<PropertyKey, unknown>
    ? [Camelize<F>, ...ArrToCamelize<R>]
    : F extends any[]
    ? [...ArrToCamelize<F>, ...ArrToCamelize<R>]
    : [F, ...ArrToCamelize<R>]
  : [];

type Camelize<T extends Record<PropertyKey, unknown>> = {
  [P in keyof T as P extends string ? CamelCase<P> : P]: T[P] extends Record<
    PropertyKey,
    unknown
  >
    ? Camelize<T[P]>
    : T[P] extends any[]
    ? ArrToCamelize<T[P]>
    : T[P];
};
3.25 Drop String

依次处理删除每个字符。

type SingleDropString<
  T extends string,
  C extends string
> = T extends `${infer F}${C}${infer R}` ? `${F}${SingleDropString<R, C>}` : T;

type DropString<
  T extends string,
  C extends string
> = C extends `${infer F}${infer R}`
  ? DropString<SingleDropString<T, F>, R>
  : T;
3.26 Split

当第二个泛型为空字符串时,就将每个字符分开即可,如果T时string,则返回一个string[].

type Split<T extends string, C extends string = ""> = string extends T
  ? string[]
  : C extends ""
  ? T extends `${infer F}${infer R}`
    ? [F, ...Split<R, C>]
    : []
  : T extends `${infer F}${C}${infer R}`
  ? [F, ...Split<R, C>]
  : [T];

3.28 ClassPublicKeys

在Typescript中,当使用 class A 定义一个类,其实,定义了两个类型 在这个类中:

  • A 是 A 类实例的类型
  • typeof A是类对象的类型

因此,类 A 的任何实例都只是一个对象,keyof 关键字返回其公共字段。

type ClassPublicKeys<A> = keyof A
3.29 IsRequireKey

我们先利用联合类型特性判断,会生成 true | false / true 等这样的联合类型。然后,把联合类型变成交集。如果是相等的值,要么是 true 要么是 false。不相等就是 never,never我们就直接返回false

type UnionToIntersection<T> = (T extends unknown ? (x: T) => unknown: never) extends (x: infer R) => unknown ? R : never;
type RequiredKeys<T extends Record<PropertyKey, unknown>> = keyof {
  [P in keyof T as T[P] extends Required<T>[P] ? P : never]: T[P]; 
};

type IsRequiredKey<
  T extends Record<PropertyKey, unknown>,
  U extends keyof T
> = [
  UnionToIntersection<
    U extends unknown ? (U extends RequiredKeys<T> ? true : false) : never
  >
] extends [infer R]
  ? [R] extends [never]
    ? false
    : R
  : never;
3.30 ObjectFromEntries

一个个取出生成对象,然后交叉合并对象。

type UnionToInterSectionFn<T> = (T extends T ? (x: () => T) => unknown: never) extends (x: infer R) => unknown ? R : never; 
type GetUnionLast<T> = UnionToInterSectionFn<T> extends () => infer R ? R : never;
type FlattenObject<T extends Record<PropertyKey, unknown>> = {
  [P in keyof T]: T[P];
};
type ObjectFromEntriesMiddle<
  T extends [PropertyKey, unknown],
  Last = GetUnionLast<T>
> = [T] extends [never]
  ? {}
  : Last extends [PropertyKey, unknown]
  ? {
      [P in Last[0]]: Last[1];
    } & ObjectFromEntriesMiddle<Exclude<T, Last>>
  : never;
type ObjectFromEntries<T extends [PropertyKey, unknown]> = FlattenObject<ObjectFromEntriesMiddle<T>>
3.31 IsPalindrome

向把字符串/数字,转化成数组,然后两头开始匹配,如果不满足匹配条件,判断数组大小是否 为 0 | 1, 如果是则返回 true,否则返回 false

type StringToArr<T extends string> = T extends `${infer F}${infer R}` ? [F, ...StringToArr<R>]: [];
type IsPalindromeMiddle<T extends string[]> = T extends [
  T[0],
  ...infer R extends string[],
  T[0]
]
  ? IsPalindromeMiddle<R>
  : T["length"] extends 0 | 1
  ? true
  : false;
type IsPalindrome<T extends string | number> = IsPalindromeMiddle<
  StringToArr<`${T}`>
>;

3.32 Mutalble Keys

使用 Equal 进行严格判断。

type IsReadonly<
  T extends Record<PropertyKey, unknown>,
  U extends keyof T,
  O = Pick<T, U>
> = Equal<O, Readonly<O>>;
type MutableKeys<T extends Record<PropertyKey, unknown>> = keyof {
  [P in keyof T as IsReadonly<T, P> extends false ? P : never]: T[P];
};
3.33 Intersection

先把内层数组展开成联合类型,获取最外层数组总的联合类型。该联合类型,和当前联合类型取交集的结果,在和下一个元素取交集,循环往复,直到所有元素都用完。

// 1. 数组展开成联合类型元素
type Flat<T extends unknown[]> = T extends [infer F, ...infer R]
  ? F extends unknown[]
    ? [F[number], ...Flat<R>]
    : [F, ...Flat<R>]
  : [];

type IntersectionMiddle<
  T extends unknown[],
  Res extends any = T[number]
> = T extends [infer F, ...infer R]
  ? (Res extends F ? Res : never) extends infer G
    ? IntersectionMiddle<R, G>
    : never
  : Res;
// 2. 将联合类型转换成交叉类型
type Intersection<T extends unknown[]> = IntersectionMiddle<Flat<T>>;
3.34 Binary to Decimal

推荐写法:设二进制数第n位为1,它这一位的十进制数为 1x2^(n-1) 所以,我们当遇到 1 时就计算它的值并累加起来即可。

type DoubleTuple<T extends unknown[]> = [...T, ...T];
type CalcPureBinary<T extends string> = T extends `${string}${infer R}`
  ? DoubleTuple<CalcPureBinary<R>>
  : [unknown];

type BinaryToDecimalTupleResult<T extends string> =
  T extends `${infer F}${infer R}`
    ? F extends "1"
      ? [...CalcPureBinary<R>, ...BinaryToDecimalTupleResult<R>]
      : BinaryToDecimalTupleResult<R>
    : [];
type BinaryToDecimal<T extends string> = BinaryToDecimalTupleResult<T>["length"];
type NumberMap = {
  "0": 0;
  "1": 1;
  "2": 2;
  "3": 3;
  "4": 4;
  "5": 5;
  "6": 6;
  "7": 7;
  "8": 8;
  "9": 9;
};
// 两数相加
type Add<
  A extends number,
  B extends number,
  CountA extends 0[] = [],
  CountB extends 0[] = []
> = CountA["length"] extends A
  ? CountB["length"] extends B
    ? [...CountA, ...CountB]["length"]
    : Add<A, B, CountA, [0, ...CountB]>
  : CountB["length"] extends B
  ? Add<A, B, [0, ...CountA], CountB>
  : Add<A, B, [0, ...CountA], [0, ...CountB]>;

// 两数相乘
// 2 x 5 = 2 + 2 + 2 + 2
type Mul<
  A extends number,
  B extends number,
  Res extends number = 0,
  Cnt extends 0[] = []
> = Cnt["length"] extends B ? Res : Mul<A, B, Add<A, Res>, [0, ...Cnt]>;

type BinaryToDecimal<
  T extends string,
  Res extends number = 0
> = T extends `${infer F}${infer R}`
  ? F extends keyof NumberMap
    ? BinaryToDecimal<R, Add<Mul<Res, 2>, NumberMap[F]>>
    : never
  : Res;
3.35 Object Key Paths

维护一个前缀即可,注意数组有两种访问方式,所以需要特殊判断。

type ObjectKeyPaths<
  T,
  PropertyBasePath extends string = "",
  Key extends keyof T = keyof T
> = Key extends string | number
  ?
      | `${PropertyBasePath}${Key}`
      | (T extends any[] ? `${PropertyBasePath}[${Key}]` : never)
      | (T[Key] extends object
          ? T[Key] extends any[]
            ? ObjectKeyPaths<
                T[Key],
                `${PropertyBasePath}${Key}.` | `${PropertyBasePath}${Key}`
              >
            : ObjectKeyPaths<T[Key], `${PropertyBasePath}${Key}.`>
          : never)
  : never;
3.36 Two Sum

注意数组中可能存在相同的数,所以,我们并不能直接使用数组展开成联合类型。我们需要使用数组下标展开成联合类型,然后利用分布式特性两两相加判断。

type Add<
  A extends number,
  B extends number,
  CountA extends 0[] = [],
  CountB extends 0[] = []
> = CountA["length"] extends A
  ? CountB["length"] extends B
    ? [...CountA, ...CountB]["length"]
    : Add<A, B, CountA, [0, ...CountB]>
  : CountB["length"] extends B
  ? Add<A, B, [0, ...CountA], CountB>
  : Add<A, B, [0, ...CountA], [0, ...CountB]>;
type ArrKeys<
  T extends any[],
  I extends 0[] = []
> = I["length"] extends T["length"]
  ? never
  : I["length"] | ArrKeys<T, [0, ...I]>;
type TwoSum<
  T extends number[],
  Target extends number,
  U extends number = ArrKeys<T>,
  K extends number = U
> = Target extends (U extends number ? Exclude<K, U> extends infer R ? R extends number ? Add<T[U], T[R]>: never: never: never) ? true : false;
3.37 ValidDate

一种思路,就是生成所有组合,然后判断传传入的日期是否在这个组合内。

type DaysOfFeb = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28
type DaysOfSM = DaysOfFeb | 29 | 30
type DaysOfBM = DaysOfSM | 31

type MonthDays = {
  1: DaysOfBM,
  2: DaysOfFeb,
  3: DaysOfBM,
  4: DaysOfSM,
  5: DaysOfBM,
  6: DaysOfSM,
  7: DaysOfBM,
  8: DaysOfBM,
  9: DaysOfSM,
  10: DaysOfBM,
  11: DaysOfSM,
  12: DaysOfBM,
}

type Months = keyof MonthDays

type FormatDate<T extends string | number> = 
  T extends unknown
  ? `${T}` extends `${1|2|3|4|5|6|7|8|9}`
    ? `0${T}`
    : `${T}`
  : never

type ValidDates<M extends Months = Months> = 
  M extends unknown 
  ? `${FormatDate<M>}${FormatDate<MonthDays[M]>}`
  : never

type ValidDate<T extends string> = T extends ValidDates ? true : false

大概思路就时判断是否在正确的范围内即可。

type DateMap = {
  "01": 31;
  "03": 31;
  "05": 31;
  "07": 31;
  "08": 31;
  "10": 31;
  "12": 31;
  "04": 30;
  "06": 30;
  "09": 30;
  "11": 30;
  "02": 28;
};
type NumberMap = {
  "0": 0;
  "1": 1;
  "2": 2;
  "3": 3;
  "4": 4;
  "5": 5;
  "6": 6;
  "7": 7;
  "8": 8;
  "9": 9;
};
// 两数相加
type Add<
  A extends number,
  B extends number,
  CountA extends 0[] = [],
  CountB extends 0[] = []
> = CountA["length"] extends A
  ? CountB["length"] extends B
    ? [...CountA, ...CountB]["length"]
    : Add<A, B, CountA, [0, ...CountB]>
  : CountB["length"] extends B
  ? Add<A, B, [0, ...CountA], CountB>
  : Add<A, B, [0, ...CountA], [0, ...CountB]>;

// 两数相乘
// 2 x 5 = 2 + 2 + 2 + 2
type Mul<
  A extends number,
  B extends number,
  Res extends number = 0,
  Cnt extends 0[] = []
> = Cnt["length"] extends B ? Res : Mul<A, B, Add<A, Res>, [0, ...Cnt]>;
type ToNumber<
  T extends string,
  Res extends number = 0
> = T extends `${infer F}${infer R}`
  ? F extends keyof NumberMap
    ? ToNumber<
        R,
        NumberMap[F] extends 0 ? Mul<Res, 10> : Add<Mul<Res, 10>, NumberMap[F]>
      >
    : never
  : Res;
type GreaterThan<
  D1 extends number,
  D2 extends number,
  hasEqual extends boolean = false,
  A extends unknown[] = []
> = D1 extends D2
  ? hasEqual
  : A["length"] extends D1
  ? false
  : A["length"] extends D2
  ? true
  : GreaterThan<D1, D2, hasEqual, [unknown, ...A]>;
type ValidDate<T extends string> =
  T extends `${infer M1}${infer M2}${infer D1}${infer D2}${infer R}`
    ? R extends ""
      ? `${M1}${M2}` extends keyof DateMap
        ? GreaterThan<ToNumber<`${D1}${D2}`>, 1, true> extends true
          ? GreaterThan<
              DateMap[`${M1}${M2}`],
              ToNumber<`${D1}${D2}`>,
              true
            > extends true
            ? true
            : false
          : false
        : false
      : false
    : false;
3.38 Assign

先处理两个合并的情况,在处理多个合并的情况

type TwoAssign<
  T extends Record<PropertyKey, unknown>,
  U extends Record<PropertyKey, unknown>,
  O extends PropertyKey = keyof U | keyof T
> = {
  [P in O]: P extends keyof U
    ? U[P] extends Record<PropertyKey, unknown>
      ? T[P] extends Record<PropertyKey, unknown>
        ? TwoAssign<T[P], U[P]>
        : U[P]
      : U[P]
    : U[P] extends any[]
    ? U[P]
    : T[P];
};

type Assign<T extends Record<PropertyKey, unknown>, O> = O extends [
  infer F extends Record<PropertyKey, unknown>,
  ...infer R extends Record<PropertyKey, unknown>[]
]
  ? Assign<TwoAssign<T, F>, R>
  : T;

3.39 Maximum

我们需要一个比较两个数值大小的函数,用一个标记来判断是否是第一次递归,如果第一次走到了最后一个分支,那么直接返回never,说明该数组为空。否则,返回Res该结果。

type GreaterThan<
  D1 extends number,
  D2 extends number,
  hasEqual extends boolean = false,
  A extends unknown[] = []
> = D1 extends D2
  ? hasEqual
  : A["length"] extends D1
  ? false
  : A["length"] extends D2
  ? true
  : GreaterThan<D1, D2, hasEqual, [unknown, ...A]>;
type TwoMaximum<A extends number, B extends number> = GreaterThan<
  A,
  B
> extends true
  ? A
  : B;
type Maximum<
  T extends number[],
  Res extends number = 0,
  IsFirst extends boolean = true
> = T extends [infer F extends number, ...infer R extends number[]]
  ? Maximum<R, TwoMaximum<Res, F>, false>
  : IsFirst extends true
  ? never
  : Res;

3.40 Capitalize Nest Object Keys
type CapitalizeNestObjetKeysInArray<T extends any[]> = T extends [
  infer F,
  ...infer R
]
  ? [CapitalizeNestObjectKeys<F>, ...CapitalizeNestObjetKeysInArray<R>]
  : [];
type CapitalizeNestObjectKeys<T> = T extends Record<PropertyKey, unknown>
  ? {
      [P in keyof T as P extends string
        ? Capitalize<Lowercase<P>>
        : P]: CapitalizeNestObjectKeys<T[P]>;
    }
  : T extends any[]
  ? CapitalizeNestObjetKeysInArray<T>
  : T;
3.41 Replace Union

把联合类型依次取出来,然后再数组种查找是否有匹配的,匹配则转换。

type UnionToInterSectionFn<T> = (T extends T ? (x: () => T) => unknown : never) extends (x: infer R) => unknown ? R: never;
type GetUnionLast<T> = UnionToInterSectionFn<T> extends () => infer R ? R : never;

type OneReplace<T, U extends [any, any][]> = U extends [
  infer F extends [any, any],
  ...infer R extends [any, any][]
]
  ? T extends F[0]
    ? F[1]
    : OneReplace<T, R>
  : T;
type UnionReplace<T, U extends [any, any][], Last = GetUnionLast<T>> = [
  T
] extends [never]
  ? never
  : OneReplace<Last, U> | UnionReplace<Exclude<T, Last>, U>;

3.42 FizzBuzz

分别判断是不是3和5的倍数即可。

type GreaterThan<A extends number, B extends number, T extends 0[] = []> = 
  A extends B ? 
    false
    : T["length"] extends A ?
      false
    : T["length"] extends B ?
      true
    : GreaterThan<A, B, [0, ...T]>;

type IsNumberMul<
  T extends number,
  Base extends 0[],
  I extends 0[] = []
> = I["length"] extends T
  ? true
  : GreaterThan<I["length"], T> extends true
  ? false
  : IsNumberMul<T, Base, [...I, ...Base]>;
type IsThreeMul<T extends number> = IsNumberMul<T, [0, 0, 0]>;
type IsFiveMul<T extends number> = IsNumberMul<T, [0, 0, 0, 0, 0]>;

type FizzBuzz<
  N extends number,
  I extends 0[] = [0],
  Res extends any[] = []
> = GreaterThan<I["length"], N> extends true
  ? Res
  : IsThreeMul<I["length"]> extends true
  ? IsFiveMul<I["length"]> extends true
    ? FizzBuzz<N, [0, ...I], [...Res, "FizzBuzz"]>
    : FizzBuzz<N, [0, ...I], [...Res, "Fizz"]>
  : IsFiveMul<I["length"]> extends true
  ? FizzBuzz<N, [0, ...I], [...Res, "Buzz"]>
  : FizzBuzz<N, [0, ...I], [...Res, `${I["length"]}`]>;
3.43 Run-length encoding

编码时:统计重复字符,当遇见和前面统计字符不一样时,则将其压缩进字符串,然后重新统计。

解码时:取出每个字符前面的数字,一个字符的情况需要特殊判断。然后,将字符拼接到长度 和 数字一样即可。

namespace RLE {
  export type Encode<
    S extends string,
    PreChar extends string = "",
    Count extends 0[] = []
  > = S extends ""
    ? PreChar extends ""
      ? ""
      : Count["length"] extends 1 | 0
      ? PreChar
      : `${Count["length"]}${PreChar}`
    : S extends `${infer F}${infer R}`
    ? F extends PreChar
      ? Encode<R, PreChar, [0, ...Count]>
      : Count["length"] extends 1 | 0
      ? `${PreChar}${Encode<R, F, [0]>}`
      : `${Count["length"]}${PreChar}${Encode<R, F, [0]>}`
    : "";

  export type Decode<
    S extends string,
    PreNumStr extends string = ""
  > = S extends `${infer F}${infer R}`
    ? IsLetter<F> extends true
      ? PreNumStr extends ""
        ? `${F}${Decode<R, "">}`
        : `${Repeat<F, ToNumber<PreNumStr>>}${Decode<R, "">}`
      : Decode<R, `${PreNumStr}${F}`>
    : "";

  type Repeat<
    T extends string,
    N extends number,
    Count extends 0[] = []
  > = Count["length"] extends N ? "" : `${T}${Repeat<T, N, [0, ...Count]>}`;
  type IsLetter<T extends string> = Uppercase<T> extends Lowercase<T>
    ? false
    : true;
  type ToNumber<
    T extends string,
    Res extends number = 0
  > = T extends `${infer F}${infer R}`
    ? F extends keyof NumberMap
      ? ToNumber<
          R,
          NumberMap[F] extends 0
            ? Mul<Res, 10>
            : Add<Mul<Res, 10>, NumberMap[F]>
        >
      : never
    : Res;
  type NumberMap = {
    "0": 0;
    "1": 1;
    "2": 2;
    "3": 3;
    "4": 4;
    "5": 5;
    "6": 6;
    "7": 7;
    "8": 8;
    "9": 9;
  };
  // 两数相加
  type Add<
    A extends number,
    B extends number,
    CountA extends 0[] = [],
    CountB extends 0[] = []
  > = CountA["length"] extends A
    ? CountB["length"] extends B
      ? [...CountA, ...CountB]["length"]
      : Add<A, B, CountA, [0, ...CountB]>
    : CountB["length"] extends B
    ? Add<A, B, [0, ...CountA], CountB>
    : Add<A, B, [0, ...CountA], [0, ...CountB]>;

  // 两数相乘
  // 2 x 5 = 2 + 2 + 2 + 2
  type Mul<
    A extends number,
    B extends number,
    Res extends number = 0,
    Cnt extends 0[] = []
  > = Cnt["length"] extends B ? Res : Mul<A, B, Add<A, Res>, [0, ...Cnt]>;
}
3.44 Tree path array

利用联合类型展开,用一个数组保存前缀路径

type Path<
  T extends Record<PropertyKey, unknown>,
  Pre extends any[] = [],
  U = keyof T
> = U extends keyof T
  ?
      | [...Pre, U]
      | (T[U] extends Record<PropertyKey, unknown>
          ? Path<T[U], [...Pre, U]>
          : never)
  : never;
3.45 SnakeCase

直接边判断边处理

type IsUppercase<T extends string> = Uppercase<T> extends T ? true : false;

type SnakeCase<
  T extends string,
  _Pre extends string = ""
> = T extends `${infer F}${infer R}`
  ? IsUppercase<F> extends true
    ? SnakeCase<R, `${_Pre}_${Lowercase<F>}`>
    : SnakeCase<R, `${_Pre}${F}`>
  : _Pre;

先将驼峰字符串以大写字母分隔,并转化为小写,生成一个字符串数组。然后通过 Join 将该数组指定字符连接起来

type SplitByCapitalize<
  T extends string,
  PreStr extends string = "",
  Res extends string[] = []
> = T extends `${infer F}${infer R}`
  ? F extends Uppercase<F>
    ? PreStr extends ""
      ? SplitByCapitalize<R, F, Res>
      : SplitByCapitalize<R, F, [...Res, Lowercase<PreStr>]>
    : SplitByCapitalize<R, `${PreStr}${F}`, Res>
  : PreStr extends ""
  ? Res
  : [...Res, Lowercase<PreStr>];

type Join<
  T extends string[],
  C extends string,
  Res extends string = ""
> = T extends [infer F extends string, ...infer R extends string[]]
  ? Res extends ""
    ? Join<R, C, F>
    : Join<R, C, `${Res}${C}${F}`>
  : Res;
type SnakeCase<T extends string> = T extends string
  ? Join<SplitByCapitalize<T>, "_">
  : never;

4. Extreme Part

4.1 Get Readonly Keys

将需要判断的键Pick成一个单独的对象O, 然后, Equal<O, Readonly< O >> 比较是否相等。

type IsReadonly<T, U extends keyof T, _O = Pick<T, U>> = Equal<_O, Readonly<_O>>;
type GetReadonlyKeys<
  T,
  _U extends keyof T = keyof T
> = _U extends keyof T ? (IsReadonly<T, _U> extends true ? _U : never) : never;
4.2 Query String Parser

推荐写法:

type Tokenize<T extends string> = Split<T, "&">;
type ParseValue<T extends string[]> = T extends [
  infer F,
  ...infer R extends string[]
]
  ? F extends `${infer K}=${infer V}`
    ? [[K, V], ...ParseValue<R>]
    : [[F, true], ...ParseValue<R>]
  : [];
type MyReplace<O, K, V> = { [P in Exclude<keyof O, K>]: O[P] } & {
  [P in K & string]: V;
};
type ReduceKV<
  T extends [string, unknown][],
  _ACC extends Record<string, unknown[]> = {}
> = T extends [
  [infer K extends string, infer V],
  ...infer Rest extends [string, unknown][]
]
  ? K extends keyof _ACC
    ? V extends _ACC[K][number] // 避免添加重复的类型。直接在这里处理该逻辑,既能利用已经获取到的 V,又能直接从 _ACC[K] 中并出现有的所有类型,方便
      ? ReduceKV<Rest, _ACC>
      : ReduceKV<Rest, MyReplace<_ACC, K, [..._ACC[K], V]>>
    : ReduceKV<Rest, _ACC & { [P in K]: [V] }>
  : _ACC;
type ExpandSingleValue<T> = {
  [P in keyof T]: T[P] extends [infer V] ? V : T[P];
};

type ParseQueryString<S extends string> = ExpandSingleValue<
  ReduceKV<ParseValue<Tokenize<S>>>
>;

思路:

  • 先把字符串以 & 分成数组
  • 把数组中每个字符串 以 = 分成数组
  • 递归生成对象,注意合并两个对象必须,去重或直接用数组包裹起来。只有一个值的数组,键值应该是 false
type FlattenObject<T> = {
  [P in keyof T]: T[P]
}
type Intersepted = (() => "a") & (() => "b") & (() => "c") extends () => infer R
? R
: never; // "c"

type UnionToInterSectionFn<T> = (
T extends T ? (x: () => T) => unknown : never
) extends (x: infer R) => unknown
? R
: never;

type GetUnionLast<T> = UnionToInterSectionFn<T> extends () => infer R
? R
: never;
type UnionToTuple<T, Res extends unknown[] = [], Last = GetUnionLast<T>> = [
T
] extends [never]
? Res
: UnionToTuple<Exclude<T, Last>, [Last, ...Res]>;
// 先将字符串以 & 分割
// 将每个元素以 = 号分割
// 长度为 1 的则设置为true,长度为 2 的则设置值

type SplitByF8<T extends string> = Split<T, "&">;
type SplitByF12<T extends string> = Split<T, "=">;
// 合并两个对象,合并不同的部分,合并相同的部分
type TwoUnionIntersection<A, B> = A extends B ? A : never;
type Split<T extends string, C extends string = ""> = string extends T
  ? string[]
  : C extends ""
  ? T extends `${infer F}${infer R}`
    ? [F, ...Split<R, C>]
    : []
  : T extends `${infer F}${C}${infer R}`
  ? [F, ...Split<R, C>]
  : [T];
type TwoObjectMerge<
  T extends Record<PropertyKey, unknown>,
  U extends Record<PropertyKey, unknown>
> = {
  [P in Exclude<keyof T, keyof U>]: T[P];
} & {
  [P in Exclude<keyof U, keyof T>]: U[P];
} & {
  [P in TwoUnionIntersection<keyof U, keyof T>]: T[P] extends any[]
    ? U[P] extends any[]
      ? UnionToTuple<T[P][number] | U[P][number]>
      : UnionToTuple<T[P][number] | U[P]>
    : U[P] extends any[]
    ? UnionToTuple<U[P][number] | T[P]>
    : T[P] extends U[P]
    ? T[P]
    : [T[P], U[P]];
};
type SplitQuery<
  T extends string,
  Res extends any[] = [],
  U extends string[] = SplitByF8<T>
> = U extends [infer F extends string, ...infer R extends string[]]
  ? SplitQuery<T, [...Res, SplitByF12<F>], R>
  : Res;
type MergeObject<T extends any[][]> = T extends [
  infer F extends any[],
  ...infer R extends any[][]
]
  ? TwoObjectMerge<
      {
        [P in F[0]]: F["length"] extends 2 ? F[1] : true;
      },
      MergeObject<R>
    >
  : {};
type ParseQueryString<T extends string> = T extends ""
  ? {}
  : FlattenObject<MergeObject<SplitQuery<T>>>;
4.3 Slice

想办法把负数转化成正数。

type GreaterThan<
    D1 extends number,
    D2 extends number,
    hasEqual extends boolean = false,
    A extends unknown[] = []
  > = D1 extends D2
    ? hasEqual
    : A["length"] extends D1
    ? false
    : A["length"] extends D2
    ? true
    : GreaterThan<D1, D2, hasEqual, [unknown, ...A]>;

  // 遇到负数直接把数组反转过来即可
  // 判断是否是负数
  type IsNegative<T extends number> = `${T}` extends `-${number}`
    ? true
    : false;
  type NegativeToPositive<T extends number> =
    `${T}` extends `-${infer R extends number}` ? R : never;

  type NegativeModArrIndex<
    T extends number,
    Arr extends any[],
    I extends 0[] = []
  > = IsNegative<T> extends true
    ? Arr extends [any, ...infer R]
      ? [0, ...I]["length"] extends NegativeToPositive<T>
        ? R["length"]
        : NegativeModArrIndex<T, R, [0, ...I]>
      : Arr["length"]
    : T;

  type PositiveSlice<
    T extends any[],
    Start extends number,
    End extends number,
    I extends 0[] = [],
    CrossStart extends boolean = Start extends 0 ? true : false
  > = T extends [infer F, ...infer R]
    ? CrossStart extends true
      ? I["length"] extends End
        ? []
        : [F, ...PositiveSlice<R, Start, End, [0, ...I], CrossStart>]
      : I["length"] extends Start
      ? [F, ...PositiveSlice<R, Start, End, [0, ...I], true>]
      : PositiveSlice<R, Start, End, [0, ...I], CrossStart>
    : [];

 type Slice<
    T extends any[],
    Start extends number = 0,
    End extends number = T["length"]
  > = GreaterThan<
    NegativeModArrIndex<Start, T>,
    NegativeModArrIndex<End, T>,
    true
  > extends true
    ? []
    : PositiveSlice<
        T,
        NegativeModArrIndex<Start, T>,
        NegativeModArrIndex<End, T>
      >;
4.4 Integers Comparator

先把数字用 0 补充到相同的长度,然后,按位比较大小。一正一负的情况单独处理。都是负数的情况要特判。

enum Comparison {
  Greater,
  Equal,
  Lower,
}
// 判断是否是负数
type IsNegative<T extends number> = `${T}` extends `-${number}` ? true : false;
type NegativeToPositive<T extends number> =
  `${T}` extends `-${infer R extends number}` ? R : never;
// 比较 0~9 这样的两个数大小, 判断 A > B 是否成立
type GreaterThan<
  A extends number,
  B extends number,
  I extends 0[] = []
> = A extends B
  ? false
  : I["length"] extends A
  ? I["length"] extends B
    ? false
    : false
  : I["length"] extends B
  ? true
  : GreaterThan<A, B, [0, ...I]>;
// 获取最大值
type GetMax<A extends number, B extends number> = GreaterThan<
  A,
  B
> extends true
  ? A
  : B;

// 获取字符串长度
type GetStringLength<
  S extends string,
  I extends 0[] = []
> = S extends `${string}${infer R}`
  ? GetStringLength<R, [0, ...I]>
  : I["length"];

// 将字符串用0补全到指定位数
type FillZero<
  S extends string,
  N extends number
> = GetStringLength<S> extends N ? S : FillZero<`0${S}`, N>;

// 1. 两个都是正数的情况
// 2. 两个都是负数的情况
// 3. 一正一负的情况

// 传入的字符串会补到一样的长度 00000
type PositiveComparator<
  A extends string,
  B extends string,
  isNeg extends boolean = false
> = A extends B
  ? Comparison.Equal
  : A extends `${infer AF extends number}${infer AR}`
  ? B extends `${infer BF extends number}${infer BR}`
    ? AF extends BF
      ? PositiveComparator<AR, BR, isNeg>
      : GreaterThan<AF, BF> extends true
      ? isNeg extends true
        ? Comparison.Lower
        : Comparison.Greater
      : isNeg extends true
      ? Comparison.Greater
      : Comparison.Lower
    : never
  : never;
// 处理一正一负的情况
type NegAndPosComparator<
  A extends number,
  B extends number
> = `${A}` extends `-${number}` ? Comparison.Lower : Comparison.Greater;
type Comparator<
  A extends number,
  B extends number,
  MaxLen extends number = GetMax<
    GetStringLength<`${A}`>,
    GetStringLength<`${B}`>
  >
> = [IsNegative<A> | IsNegative<B>] extends [true]
  ? PositiveComparator<
      FillZero<`${NegativeToPositive<A>}`, MaxLen>,
      FillZero<`${NegativeToPositive<B>}`, MaxLen>,
      true
    >
  : [IsNegative<A> | IsNegative<B>] extends [false]
  ? PositiveComparator<FillZero<`${A}`, MaxLen>, FillZero<`${B}`, MaxLen>>
  : NegAndPosComparator<A, B>
4.5 Currying 2

利用函数的泛型,让函数执行后再进行类型推测。

 type CurriedResult<FA extends unknown[], FR> = FA extends []
    ? FR
    : <FRFA extends unknown[]>( // FRFA 是执行函数时,传入的参数列表
        ...args: FRFA
      ) => FA extends [...FRFA, ...infer FARest] // 获取剩余没有传递的参数,继续递归判断。
        ? CurriedResult<FARest, FR>
        : never;
  declare function DynamicParamsCurrying<FA extends unknown[], FR>(
    fn: (...args: FA) => FR
  ): CurriedResult<FA, FR>;
4.6 Sum

我们首先要知道按位相加,每一位相加再加上后面的进位的数。最多不超过20。所以,我们可以先预处理出所有需要进位的数字和保留位。然后,把字符串反转过来,从个位开始加。

type NeedingCarryBit = `${1 | 2}${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`;
// 20 => [0, 2]
type TransForm = {
  [P in NeedingCarryBit]: P extends `${infer F extends number}${infer R extends number}`
    ? [R, F]
    : never;
};
// ADD 较小的两个数相加
type Add<
  A extends number,
  B extends number,
  CountA extends 0[] = [],
  CountB extends 0[] = []
> = CountA["length"] extends A
  ? CountB["length"] extends B
    ? [...CountA, ...CountB]["length"]
    : Add<A, B, CountA, [0, ...CountB]>
  : CountB["length"] extends B
  ? Add<A, B, [0, ...CountA], CountB>
  : Add<A, B, [0, ...CountA], [0, ...CountB]>;
// 判断两个数字的大小
type GreaterThan<
  A extends number,
  B extends number,
  I extends 0[] = []
> = A extends B
  ? false
  : I["length"] extends A
  ? I["length"] extends B
    ? false
    : false
  : I["length"] extends B
  ? true
  : GreaterThan<A, B, [0, ...I]>;
// 反转字符串
type ReverseString<S extends string> = S extends `${infer F}${infer R}`
  ? `${ReverseString<R>}${F}`
  : "";
// 获取字符串长度
type GetStringLength<
  S extends string,
  I extends 0[] = []
> = S extends `${string}${infer R}`
  ? GetStringLength<R, [0, ...I]>
  : I["length"];
type StringNumberAdd<
  A extends string,
  B extends string,
  Jw extends number = 0
> = A extends `${infer AF extends number}${infer AR}`
  ? B extends `${infer BF extends number}${infer BR}`
    ? Add<AF, Add<BF, Jw>> extends infer AFBF extends number
      ? `${AFBF}` extends keyof TransForm
        ? `${TransForm[`${AFBF}`][0]}${StringNumberAdd<
            AR,
            BR,
            TransForm[`${AFBF}`][1]
          >}`
        : `${AFBF}${StringNumberAdd<AR, BR, 0>}`
      : never
    : Jw extends 0
    ? A
    : Add<Jw, AF> extends infer AFJW extends number
    ? `${AFJW}` extends keyof TransForm
      ? `${TransForm[`${AFJW}`][0]}${StringNumberAdd<
          AR,
          "",
          TransForm[`${AFJW}`][1]
        >}`
      : `${AFJW}${StringNumberAdd<AR, "", 0>}`
    : never
  : Jw extends 0
  ? ""
  : `${Jw}`;
type Sum<
  A extends string | number | bigint,
  B extends string | number | bigint
> = ReverseString<
  GreaterThan<GetStringLength<`${A}`>, GetStringLength<`${B}`>> extends true
    ? StringNumberAdd<ReverseString<`${A}`>, ReverseString<`${B}`>>
    : StringNumberAdd<ReverseString<`${B}`>, ReverseString<`${A}`>>
>;
4.7 Multiply

先对应相位相加,然后再处理进位,进位的时候保留最低位即可。注意,有一个数为 0 的情况。

namespace SumChallenge {
  type NeedingCarryBit = `${1 | 2}${0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}`;
  // 20 => [0, 2]
  type TransForm = {
    [P in NeedingCarryBit]: P extends `${infer F extends number}${infer R extends number}`
      ? [R, F]
      : never;
  };
  // ADD 较小的两个数相加
  export type Add<
    A extends number,
    B extends number,
    CountA extends 0[] = [],
    CountB extends 0[] = []
  > = CountA["length"] extends A
    ? CountB["length"] extends B
      ? [...CountA, ...CountB]["length"]
      : Add<A, B, CountA, [0, ...CountB]>
    : CountB["length"] extends B
    ? Add<A, B, [0, ...CountA], CountB>
    : Add<A, B, [0, ...CountA], [0, ...CountB]>;
  // 判断两个数字的大小
  export type GreaterThan<
    A extends number,
    B extends number,
    I extends 0[] = []
  > = A extends B
    ? false
    : I["length"] extends A
    ? I["length"] extends B
      ? false
      : false
    : I["length"] extends B
    ? true
    : GreaterThan<A, B, [0, ...I]>;
  // 反转字符串
  export type ReverseString<S extends string> = S extends `${infer F}${infer R}`
    ? `${ReverseString<R>}${F}`
    : "";

  // 获取字符串长度
  export type GetStringLength<
    S extends string,
    I extends 0[] = []
  > = S extends `${string}${infer R}`
    ? GetStringLength<R, [0, ...I]>
    : I["length"];

  export type StringNumberAdd<
    A extends string,
    B extends string,
    Jw extends number = 0
  > = A extends `${infer AF extends number}${infer AR}`
    ? B extends `${infer BF extends number}${infer BR}`
      ? Add<AF, Add<BF, Jw>> extends infer AFBF extends number
        ? `${AFBF}` extends keyof TransForm
          ? `${TransForm[`${AFBF}`][0]}${StringNumberAdd<
              AR,
              BR,
              TransForm[`${AFBF}`][1]
            >}`
          : `${AFBF}${StringNumberAdd<AR, BR, 0>}`
        : never
      : Jw extends 0
      ? A
      : Add<Jw, AF> extends infer AFJW extends number
      ? `${AFJW}` extends keyof TransForm
        ? `${TransForm[`${AFJW}`][0]}${StringNumberAdd<
            AR,
            "",
            TransForm[`${AFJW}`][1]
          >}`
        : `${AFJW}${StringNumberAdd<AR, "", 0>}`
      : never
    : Jw extends 0
    ? ""
    : `${Jw}`;

  export type Sum<
    A extends string | number | bigint,
    B extends string | number | bigint
  > = ReverseString<
    GreaterThan<GetStringLength<`${A}`>, GetStringLength<`${B}`>> extends true
      ? StringNumberAdd<ReverseString<`${A}`>, ReverseString<`${B}`>>
      : StringNumberAdd<ReverseString<`${B}`>, ReverseString<`${A}`>>
  >;

  // 0 ~ 9 范围内的两个数相加, [a, b] a表示剩余数,b表示进位数
}

namespace MutiplyChallenge {
  // 0. 生成一个保存每一相位的值的数组
  type InitialZeroArr<
    N extends number,
    Res extends string[] = []
  > = Res["length"] extends N ? Res : InitialZeroArr<N, [...Res, "0"]>;
  type LitterNumberMul<
    Base extends string,
    B extends string,
    I extends 0[] = [],
    Res extends string = "0"
  > = `${I["length"]}` extends B
    ? Res
    : LitterNumberMul<Base, B, [0, ...I], SumChallenge.Sum<Base, Res>>;

  type StringToRevArr<S extends string> = S extends `${infer F}${infer R}` ? [...StringToRevArr<R>, F] : [];
  type ChangeArrByIndex<Arr extends string[], Tindex extends number, Value, I extends 0[] = []> = 
    Arr extends [infer F, ...infer R extends string[]] ? 
      I["length"] extends Tindex ? [Value, ...R] 
      : [F, ...ChangeArrByIndex<R, Tindex, Value, [0, ...I]>] 
    : [];
  type GetArrValueByIndex<Arr extends string[], Index extends number, I extends 0[] = []> =
    Arr extends [infer F, ...infer R extends string[]] ? 
      I["length"] extends Index ? 
        F 
        : GetArrValueByIndex<R, Index, [0, ...I]> 
    : never;
 
  type MutiplyFirst<A extends string[], B extends string[], Res extends string[] = [], I1 extends 0[] = [], I2 extends 0[] = [], ResI extends number = SumChallenge.Add<I1["length"], I2["length"]>> =
    I2["length"] extends B["length"] ? 
      I1["length"] extends A["length"] ? 
        Res 
      : MutiplyFirst<A, B, Res, [0, ...I1], []>
    : MutiplyFirst<A, B, 
      ChangeArrByIndex<Res, ResI, SumChallenge.ReverseString<SumChallenge.Sum<SumChallenge.ReverseString<Res[ResI]>, LitterNumberMul<A[I1["length"]], B[I2["length"]]>>>>
    , I1, [0, ...I2]>;
  
  type MutiplySeconde<C extends string[], I extends 0[] = [], JW extends string = "0", Res extends string[] = []> = 
    I["length"] extends C["length"] ? 
      Res 
      : SumChallenge.ReverseString<SumChallenge.Sum<SumChallenge.ReverseString<C[I["length"]]>, SumChallenge.ReverseString<JW>>> extends `${infer F}${infer R}` ? 
          R extends "" ? 
            MutiplySeconde<C, [0, ...I], "0", [...Res, F]>
            : MutiplySeconde<C, [0, ...I], R, [...Res, F]>
        : never; 
  // 拼接字符串
  type MutiplyThree<C extends string[]> = C extends [infer F extends string, ...infer R extends string[]] ? 
    `${MutiplyThree<R>}${F}`
    : "";

  // 去除前导0
  type MultiplyFour<C extends string> = C extends `0${infer R}` ? MultiplyFour<R> : C extends "" ? "0" : C;
  export type Multiply<A extends string | number | bigint, B extends string | number | bigint> = MultiplyFour<MutiplyThree<MutiplySeconde<MutiplyFirst<StringToRevArr<`${A}`>, StringToRevArr<`${B}`>, InitialZeroArr<30>>>>>;

}
type Multiply<A extends string | number | bigint, B extends string | number | bigint> = MutiplyChallenge.Multiply<A, B>;
4.8 Tag

太复杂没做

4.9 Indusive Range
type InclusiveRange<
  Lower extends number,
  Higher extends number,
  Count extends 1[] = CountToLower<`${Lower}`, `${Higher}`>,  // [0, 1, 2, 3, ... Higher]
  Result extends number[] = [],                               // [Lower, ..., Higher]
  C extends number = Count['length'],                         // 0, 1, 2, 3, ... Higher
  > =
  [Count] extends [never] ? (
    []
  ) : C extends Higher ? (
    [...Result, C]
  ) : InclusiveRange<Lower, Higher, [...Count, 1], [...Result, C]>

// never if Higher < Lower, otherwise [1, 1, 1, ...x Lower]
type CountToLower<L extends string, H extends string, Count extends 1[] = []> =
  L extends `${infer FirstL}${infer SecondL}${infer RestL}` ? (
    H extends `${string}${infer SecondH}${infer RestH}` ? (
      CountToLower<`${SecondL}${RestL}`, `${SecondH}${RestH}`, N<Count>[keyof N & FirstL]>
    ) : never
  ) : N<Count>[keyof N & L] extends [...N<Count>[keyof N & H], 1, ...1[]] ? (
    never
  ) : N<Count>[keyof N & L]

// T for ten digit, '0' for single digit
type N<T extends 1[] = []> = {
  '0': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T],
  '1': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1],
  '2': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1],
  '3': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1, 1],
  '4': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1, 1, 1],
  '5': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1, 1, 1, 1],
  '6': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1, 1, 1, 1, 1],
  '7': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1, 1, 1, 1, 1, 1],
  '8': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1, 1, 1, 1, 1, 1, 1],
  '9': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, 1, 1, 1, 1, 1, 1, 1, 1, 1],
}
4.10 Sort
type Compara<A extends number, B extends number> = A extends B ? 0 :  "0123456789" extends `${string}${A}${string}${B}${string}` ? -1 : 1;
type GetNumberMax<Arr extends number[], MaxV extends number = Arr[0]> = Arr extends [infer F extends number, ...infer R extends number[]] ? 
    Compara<F, MaxV> extends 1 ? 
      GetNumberMax<R, F>
      : GetNumberMax<R, MaxV>
    : MaxV;

type EarseNumber<Arr extends number[], Target extends number> = 
  Arr extends [infer F, ...infer R extends number[]] ? 
    F extends Target ? [...R] 
    : [F, ...EarseNumber<R, Target>] 
  : [];
type Sort<T extends number[], IsGreater extends boolean = false, Res extends number[] = [], MaxV extends number = GetNumberMax<T>> = 
  T extends [] ? 
    Res
    : IsGreater extends false ? 
        Sort<EarseNumber<T, MaxV>, IsGreater, [MaxV, ...Res]> 
        : Sort<EarseNumber<T, MaxV>, IsGreater, [...Res, MaxV]>;
;
4.11 DistributeUnions
type Merge<T> = {
    [P in keyof T]: T[P];
  };

// 1. 处理数组, 想数组展开一样处理
type DistributeArrays<T extends unknown[]> = T extends [infer F, ...infer R]
  ? ArrHelper<DistributeUnions<F>, R>
  : [];
type ArrHelper<T, U extends unknown[]> = T extends T
  ? [T, ...DistributeArrays<U>]
  : [];
// {x: string} & ({a: number} | {a: string}) => {a: number; x: string} | {a: string; x: string}
type DistributeObject<
  T extends Record<PropertyKey, unknown>,
  U extends keyof T = keyof T
> = [U] extends [never]
  ? {}
  : U extends U
  ? ObjectHelper<U, DistributeUnions<T[U]>> & DistributeObject<Omit<T, U>>
  : never;
type ObjectHelper<K, V> = V extends V ? { [P in K & string]: V } : never;
type DistributeUnions<T> = T extends unknown[]
  ? DistributeArrays<T>
  : T extends Record<PropertyKey, unknown>
  ? Merge<DistributeObject<T>>
  : T;
4.12 Assert Array Index

太复杂没做

4.13 JSON Parser

太复杂没做

4.14 Subtract

太复杂没做