본문 바로가기
Language/Typescript

타입챌린지 : 3326-BEM style string (medium)

by hsloth 2023. 9. 14.

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

 

https://github.com/type-challenges/type-challenges/blob/main/questions/03326-medium-bem-style-string/README.md

 

 

쉽게 말하면 제네릭 B를 기반으로 조합할 수 있는 E와 M을 유니온 타입으로 뽑으면 된다.

(근데 조금 애매한게, E의 경우 중복해서 정의할 수 있는지를 잘 모르겠다)

type BEM<B extends string, E extends string[], M extends string[]> = 
  M extends [infer F extends string, ...infer O extends string[]] ?
    `${BEM<B, E, []>}--${F}` | `${BEM<B, E, O extends [] ? never : O>}` :
    E extends [infer F extends string, ...infer O extends string[]] ?
      `${BEM<B, [], M>}__${F}` | `${BEM<B, O extends [] ? never : O, M>}` :
      `${B}`

type A = BEM<'btn', ['price'], []>
type B = BEM<'btn', ['price'], ['warning', 'success']>
type C = BEM<'btn', ['a','b'], ['x','y']>

 

나의 경우 재귀로 풀려고... 시도해보았다.

이 경우 type C는 다음과 같이 나온다.

 

만약 E를 중복해서 리턴하게 하고 싶은 경우 다음과 같이 작성하면 된다.

type BEM<B extends string, E extends string[], M extends string[]> = 
  M extends [infer F extends string, ...infer O extends string[]] ?
    `${BEM<B, E, []>}--${F}` | `${BEM<B, E, O extends [] ? never : O>}` :
    E extends [infer F extends string, ...infer O extends string[]] ?
      `${BEM<B, O, M>}__${F}` :
      `${B}`

E의 `${BEM<B, O, M>}__${F}` 부분만 바꿔주면 된다.

이 경우 type C는 다음과 같다.

아마 이 코드 말고, 맨위의 코드가 정답일 것이다.

 

내가 작성한 코드를 해석해보자.

type BEM<B extends string, E extends string[], M extends string[]> : 이런 부분들은 알거라고 생각하고 넘어가겠다.

M extends [infer F extends string, ...infer O extends string[]] ? : 나는 재귀로 이 문제를 해결할 것이기 때문에, 뒤에 나오는 M을 먼저 다뤘다. M에서 원소를 하나씩 꺼내자.

`${BEM<B, E, []>--${F}` | `${BEM<B, E, O extends [] ? never : O>}` : Element가 처리된 문자열을 받기 위해 `BEM<B, E, []>--${F}`를 받고 유니온 타입을 만들어 주기 위해 뒤에 | `${BEM<B, E, O extends [] ? never : O>}`를 붙여준다. 여기서 O가 빈 배열이면 never를 리턴하게 하여 M이 없는 값이 나오지 않도록 해준다.

E도 위와 같이 해석할 수 있다.

 

어렵다. 내가 푼 방법으로 하면 진짜 어렵다.

 

그래서 다른 사람들의 답을 봐보았다.

type BEM<B extends string, E extends string[], M extends string[]> = `${B}${E extends []
  ? ''
  : { [K in keyof E]: `__${E[K]}` }[number]}${M extends [] ? '' : { [K in keyof M]: `--${M[K]}` }[number]}`

 

`${B}${E extends [] ? '' : { [K in keyof E] : `__${E[K]}` }[number]} : E가 빈 배열이면 빈 문자열을 리턴하고, 아니라면 E의 key(이 경우엔 배열의 인덱스 이므로 객체를 배열로 나타내겠다는 뜻)을 뽑아서 각 키 값(0, 1, 2 ...)에 대해 값이 `__${E[K]}`가 담긴 배열을 만들고 [number]로 각 원소들을 뽑겠다는 뜻이다. 즉, E에 담긴 원소가 B_E 형태로 하나씩 유니온 타입으로 뽑힌다.

${M extends [] ? '' : { [K in keyof M] : `--${M[K]}` }[number]}` : 위와 같다.

 

이렇게 작성하면 정말 간단하다... 여러분들은 재귀같은거 쓰지 말자.