类型体操刷题系列(十二)— CamelCase/KebaCase/Diff

389 阅读1分钟

目的

Github上的类型体操,让你写出更加优雅的TS类型定义,以及探寻TS中我们不熟悉的只是,让我们开始TS的类型挑战把~2022希望通过更文的方式来督促自己学习,每日三题,坚持日更不断~~

ts 类型体操 github 我的解答

题目大纲

  1. Medium CamelCase
  2. Medium KebaCase
  3. Medium Diff

01. CamelCase

题目要求

import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<CamelCase<'foo-bar-baz'>, 'fooBarBaz'>>,
  Expect<Equal<CamelCase<'foo-Bar-Baz'>, 'foo-Bar-Baz'>>,
  Expect<Equal<CamelCase<'foo-Bar-baz'>, 'foo-BarBaz'>>,
  Expect<Equal<CamelCase<'foo-bar'>, 'fooBar'>>,
  Expect<Equal<CamelCase<'foo_bar'>, 'foo_bar'>>,
  Expect<Equal<CamelCase<'foo--bar----baz'>, 'foo-Bar---Baz'>>,
  Expect<Equal<CamelCase<'a-b-c'>, 'aBC'>>,
  Expect<Equal<CamelCase<'a-b-c-'>, 'aBC-'>>,
  Expect<Equal<CamelCase<'ABC'>, 'ABC'>>,
  Expect<Equal<CamelCase<'-'>, '-'>>,
  Expect<Equal<CamelCase<''>, ''>>,
  Expect<Equal<CamelCase<'😎'>, '😎'>>,
]

我的答案

type isNever<T> = (() => T) extends () => never ? true : false;
type Letter = {
  a: "A";
  b: "B";
  c: "C";
  d: "D";
  e: "E";
  f: "F";
  g: "G";
  h: "H";
  i: "I";
  j: "J";
  k: "K";
  l: "L";
  m: "M";
  n: "N";
  o: "O";
  p: "P";
  q: "Q";
  r: "R";
  s: "S";
  t: "T";
  u: "U";
  v: "V";
  w: "W";
  x: "X";
  y: "Y";
  z: "Z";
};

type CamelCase<
  S extends string,
  F extends any = never,
  R extends string = ""
> = S extends `${infer T}${infer P}`
  ? isNever<F> extends false
    ? T extends Letter[keyof Letter]
      ? CamelCase<`${P}`, never, `${R}-${T}`>
      : T extends keyof Letter
      ? CamelCase<`${P}`, never, `${R}${Letter[T]}`>
      : CamelCase<P, F, `${R}${T}`>
    : T extends keyof Letter
    ? CamelCase<P, never, `${R}${T}`>
    : T extends "-"
    ? P extends ""
      ? `${R}-`
      : CamelCase<P, false, `${R}`>
    : CamelCase<P, F, `${R}${T}`>
  : R;

知识点

  1. 把对应特殊的 case 用 extends 判断出来,再用递归生成类型就可以

2. Medium KebabCase

题目要求

import { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<KebabCase<'FooBarBaz'>, 'foo-bar-baz'>>,
  Expect<Equal<KebabCase<'fooBarBaz'>, 'foo-bar-baz'>>,
  Expect<Equal<KebabCase<'foo-bar'>, 'foo-bar'>>,
  Expect<Equal<KebabCase<'foo_bar'>, 'foo_bar'>>,
  Expect<Equal<KebabCase<'Foo-Bar'>, 'foo--bar'>>,
  Expect<Equal<KebabCase<'ABC'>, 'a-b-c'>>,
  Expect<Equal<KebabCase<'-'>, '-'>>,
  Expect<Equal<KebabCase<''>, ''>>,
  Expect<Equal<KebabCase<'😎'>, '😎'>>,
]

我的解答

type KebabCase<
  S extends string,
  R extends string = ""
> = S extends `${infer D}${infer P}`
  ? D extends Letter[keyof Letter]
    ? KebabCase<
        P,
        R extends "" ? `${Uncapitalize<D>}` : `${R}-${Uncapitalize<D>}`
      >
    : KebabCase<P, `${R}${D}`>
  : R;

知识点

  1. 递归和 infer 的使用

3. Medium Diff

题目要求

import { Equal, Expect } from '@type-challenges/utils'

type Foo = {
  name: string
  age: string
}
type Bar = {
  name: string
  age: string
  gender: number
}

type cases = [
  Expect<Equal<Diff<Foo, Bar>, { gender: number }>>,
  Expect<Equal<Diff<Bar,Foo>, { gender: number }>>
]

我的解答

type Diff<
  O,
  O1,
  K1 extends keyof O = keyof O,
  K2 extends keyof O1 = keyof O1,
  R = {
    [K in Exclude<K1, K2>]: O[K];
  } & {
    [K in Exclude<K2, K1>]: O1[K];
  }
> = {
  [K in keyof R]: R[K];
};

知识点

  1. Exclude和对象合并的写法