【TypeScript】类型挑战 - medium 难度个人题解2

416 阅读1分钟

题目地址:github: type-challenges

在线编辑器:TypeScript Playground

13. Capitalize

大写第一个字母

type MyCapitalize<S extends string> = S extends `${infer First}${infer Rest}`
  ? `${Uppercase<First>}${Rest}`
  : S;

type Test = MyCapitalize<'abc'>; // 'Abc'

14. Replace

实现可以替换的类型

type Replace<
  S extends string,
  From extends string,
  To extends string,
> = From extends ''
  ? S
  : S extends `${infer Prev}${From}${infer After}`
    ? `${Prev}${To}${After}`
    : S;

type Test = Replace<'hello world', 'world', 'js'>; // hello js

本题没做出来。以往对字符串的想法有误,使用 extends 对字符串进行解构,可以根据相应的模式自由解构。

15. ReplaceAll

type ReplaceAll<
  S extends string,
  From extends string,
  To extends string
> = From extends ''
  ? S
  : S extends `${infer Prev}${From}${infer After}`
    ? ReplaceAll<Replace<`${Prev}${To}${After}`, From, To>, From, To>
    : S;
    
type Test = ReplaceAll<'$ $ $!', '$', 'js'>; // js js js!

循环 Replace 即可

16. AppendArgument

追加参数

type AppendArgument<
  Fn extends (...args: any[]) => unknown,
  Arg extends unknown
> = (...args: [...Parameters<Fn>, Arg]) => ReturnType<Fn>

type TestFun = AppendArgument<(a: string) => void, number>; 
// (args_0: string, args_1: number) => void

生动地展示了为啥 Parameters<T> 的结果为啥是数组

17. Length of String

type StringLength<S extends string, A extends string[] = []> =
  S extends `${infer F}${infer R}` ? StringLength<R, [...A, F]> : A['length']

type Test = StringLength<'123'> // 3

字符串虽然有 length 属性,但是在类型里面直接 S['length'] 拿到的是 number,所以这里就简单将字符串转为数组了

18. Flatten

type Flatten<Arr extends any[]> = Arr extends [infer E, ...infer R]
  ? E extends any[]
    ? [...Flatten<E>, ...Flatten<R>]
    : [E, ...Flatten<R>]
  : Arr;

type Test = Flatten<[1, [2, [3, [4]]]]>; // [1, 2, 3, 4]

19. AppendToObject

追加属性

type AppendToObject<O extends {}, Key extends string, Value extends any> = {
  [K in (keyof O | Key)]: K extends keyof O ? O[K] : Value;
}

type Test = AppendToObject<{ k: 1 }, 'k2', number>; // { k2: number; k: 1 }

也可以使用 & 对两个进行合并,只是结果会展示为 O & {...} 感觉不够完美,所以将 keyof O | Key 进行遍历,这样结果就是一个完整的对象。

20. Absolute

返回取绝对值后的字符串

type Absolute<T extends string | number | bigint> = `${T}` extends `${infer F}${infer R}`
  ? F extends '-'
    ? R
    : `${T}`
  : `${T}`

type Test = Absolute<-123.123>; // '123.123'

21. StringToUnion

取每个字符转为 union type

type StringToUnion<S extends string> = S extends `${infer F}${infer R}`
  ? F | StringToUnion<R>
  : S extends ''
    ? never
    : S;

type Test = StringToUnion<'abc'>; // 'a' | 'b' | 'c'

22. Merge

合并对象属性,第二个覆盖第一个

type Merge<First extends {}, Second extends {}> = {
  [K in (keyof First | keyof Second)]: K extends keyof Second
    ? Second[K]
    : K extends keyof First
      ? First[K]
      : never;
}

type Test = Merge<{ k1: 'k1', k: 'k' }, { k2: 'k2', k1: 'k2' }>;
// { k1: "k2"; k: "k"; k2: "k2" }

23. CamelCase

短横线转小驼峰

type CamelCase<S extends string> = S extends `${infer F}-${infer R}`
  ? `${F}${CamelCase<MyCapitalize<R>>}`
  : S;

type Test = CamelCase<'foo-bar-baz'>; // fooBarBaz

24. KebabCase

驼峰转短横线

type KebabCase<S extends string, R extends string = ''> =
  S extends `${infer F}${infer Rest}`
    ? F extends Uppercase<F>
      ? KebabCase<Rest, R extends '' ? Lowercase<F> : `${R}-${Lowercase<F>}`>
      : KebabCase<Rest, R extends '' ? F : `${R}${F}`>
    : R;

type Test = KebabCase<'FooBarBaz'>; // foo-bar-baz

25.Diff

O1O2 进行 diff,取不同的 key 组成新对象

type DiffKey<O1 extends {}, O2 extends {}> =
  Exclude<keyof O1, keyof O2> | Exclude<keyof O2, keyof O1>;

type Diff<O1 extends {}, O2 extends {}> = {
  [K in DiffKey<O1, O2>]: K extends keyof O1
    ? O1[K]
    : K extends keyof O2
      ? O2[K]
      : never;
}

type Test = Diff<{ k: 1 , k1: 2 }, { k: 3, k2: 4 }>; // { k1: 2, k2: 4 }