본문 바로가기
Language/Typescript

타입챌린지 : 110-Capitalize (medium)

by hsloth 2023. 3. 29.

 

이 글은 제가 타입챌린지를 하면서 해석한 내용을 적는 글입니다. 틀린 내용이 있으면 댓글 달아주시면 감사하겠습니다.

 

https://github.com/type-challenges/type-challenges/blob/main/questions/00110-medium-capitalize/README.md

 

GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

Collection of TypeScript type challenges with online judge - GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

github.com

 

문자열을 제네릭 인자로 받고, 해당 문자열의 맨 앞 글자를 대문자로 리턴하는 타입이다.

 

가장 간단한 방법은 이거다.

type MyCapitalize<S extends string> = Capitalize<S>;

근데 이런식으로 답을 쓰는 사람은 없겠지...

 

그래서 일단 노가다를 해보았다.

아래는 내가 작성한 답이다.

type Upper = {
  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 MyCapitalize<S extends string> = S extends `${infer F}${infer R}` ? 
  (F extends keyof Upper ? 
    `${Upper[F]}${R}` :
    `${F}${R}`) :
  S;

/* _____________ Test Cases _____________ */
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'>>,
]

일단 Upper라는 타입을 만들어서 Upper타입에 소문자가 들어오면, 그에 대응되는 대문자를 리턴하도록 만들어준다.

type MyCapitalize<S extends string> : string타입인 S를 제네릭 인자로 받는 MyCapitalize 타입을 정의한다.

S extends `${infer F}${infer R}` ? : 여기서 infer F와 infer R을 하면, 앞에 있는 infer F는 길이가 1인 문자를 지칭하는 것 같다. 해보니까 되더라. 그래서 해석을 하면, S가 F와 R로 나뉜다면 이라는 뜻이다.

(`${Upper[F]}${R}` : `${F}${R}`) : Upper에서 F에 해당하는 타입(대문자)를 꺼내서 나머지 문자열 R과 같이 출력하고 아니라면 원래 문장 그대로 출력한다. (FOOBAR의 예제 때문에 ${F}${R}이 필요하다)

S; : 그리고 S가 F와 R로 나뉘어지지 않는다면 S를 그대로 출력한다. (S가 아니라 "" 여도 상관없다)

 

 

그리고... UpperCase라는 타입이 있다는걸 조금 늦게 알았는데, 이를 활용한 답은 이러하다.

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

S가 F과 R로 나누어진다면.. F를 UpperCase해서 R과 붙여서 출력하고, 아니면 S를 그대로 출력한다는 뜻이다.