본문 바로가기
Language/Typescript

타입챌린지 : 298-Length of String (medium)

by hsloth 2023. 4. 5.

 

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

 

https://github.com/type-challenges/type-challenges/blob/main/questions/00298-medium-length-of-string/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

 

String의 Length를 리턴하는 타입을 만드는 문제이다.

그냥 보면 쉬워보인다. 하지만... string의 length 속성은 컴파일러 입장에서 그저 number를 리턴하는 속성일 뿐...

그래서 S['length'] 와 같이 코드를 작성하면 될 줄 알았는데 안되더라...

 

그래서 Array도 따로 타입을 만들어서 찍어보았는데, Array의 length는 바로 나왔다.

그래 이거다. 핵심은 String을 Array로 만들어서 Length를 뽑는거라고 생각하고 바로 코드를 작성했다.

 

일단, 어떻게든 배열을 만들어보려고 다음의 코드를 작성했다. 하지만... 이 코드에서 재귀 때문에 Length를 뽑을 수 없었다...

// 처음 작성한 코드 (오답)
type LengthOfString<S extends string> = S extends `${infer F}${infer O}` ? [F, ...LengthOfString<O>] : [];

 

그래서 코드를 다음과 같이 바꿨다.

// 오답
type Push<T extends any[], K> = [...T, K];

type LengthOfString<S extends string, K = []> = S extends `${infer F}${infer O}` ? Push<K, F> : [];

제네릭 K를 사용하여 K를 배열로 초기화해주고, 여기서 K에 S의 문자를 하나씩 넣어서 최종적으로 K의 Length를 뽑아내려고 해보았다.

문제가 있어서 재귀는 들어가지 않았는데.. 바로 Push<K, F>가 먹히지 않았다... K가 any[] 타입이 아니랜다...

 

그래서 최종적으로 K를 any[] 타입으로 설정해주고, 재귀까지 사용한 코드는 다음과 같다.

// 정답
type LengthOfString<S extends string, K = []> = K extends any[] ?
	(S extends `${infer F}${infer O}` ?
	LengthOfString<O, Push<K, F>> : K['length']) : 
	never;


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<LengthOfString<''>, 0>>,
  Expect<Equal<LengthOfString<'kumiko'>, 6>>,
  Expect<Equal<LengthOfString<'reina'>, 5>>,
  Expect<Equal<LengthOfString<'Sound! Euphonium'>, 16>>,
]

 

type LengthOfString<S extends string, K = []> : LengthOfString타입을 정의하는데, S는 string형식이고, K는 빈 배열이다.

K extends any[] ? : K가 any[] 형태이면,

S extends `${infer F}${infer O}` ? : S를 첫 글자 F와 나머지 문자열 O로 나누고,

LengthOfString<O, Push<K, F>> : K에 F를 push한 배열을 인자로 넘겨서 LengthOfString을 한 번 더 부른다. 이 과정에서 K에 원소가 하나씩 쌓여가면서 K의 length가 늘어날 것이다.

그리고, S의 길이가 0이 되면, K['length']를 리턴하여 배열 K의 길이(즉, 문자열의 길이)를 리턴하게 된다.

 

 

그리고 다른 풀이

다른 분들의 답을 보았다...

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

T extends string[] = [] 처럼 extends와 동시에 값을 초기화하는게 가능하다는걸 배웠다...!