타입 챌린지 898 - Includes

소파의 벨로그·2025년 2월 28일

타입챌린지

목록 보기
9/131

문제 링크

문제

JavaScript의 Array.includes 함수를 타입 시스템에서 구현하세요. 타입은 두 인수를 받고, true 또는 false를 반환해야 합니다.

내 풀이

type Includes<T extends readonly any[], U> =
  T extends [infer Head,...infer Rest]?
    (Equal<Head,U> extends true?
      (true)
      :(Includes<Rest,U>))
    :(false)

재귀를 활용한 기본적인 배열 순환 방식으로 접근했다.

Includes<[{ readonly a: 'A' }], { a: 'A' }>와 같이 비슷하거나
Includes<[1 | 2], 1>와 같이 대입가능한 값도 false가 나와야해서

type-challenge에서 제공하는 Equal 타입을 통해 같은지 아닌지 판단했다.

다른사람의 풀이

type Includes<T extends readonly any[], U> = {
  [P in T[number]]: true
}[U] extends true ? true : false;

이 방법은 mapped type을 활용한 방법이지만, 제네릭으로 들어오는 배열의 타입이 string,number,symbol이 아닌 경우 탐지가 불가능 했다

type Includes<T extends readonly any[], U> = T extends [infer L, ...infer R]
  ? [U, L] extends [L, U]
    ? true
    : Includes<R, U>
  : false

이 방법도 비교하는 타입과 비교당하는 타입이 서로 상속될 때만 허용이 되기 때문에
{ readonly a: 'A' }{ a: 'A'} 같은 타입은 탐지 할 수 없었다.
다만, conditional type 을 두 번 쓸 여지를 줄여주었다는 점이 핵심이다

관련 개념

Equal 타입은 템플릿 같은 느낌으로 사용하기 때문에, 원리를 이해하기보다는 그냥 사용하려고 한다
Equal 타입의 구현체는 다음과 같다

type Equal<X, Y> =
	(<T>() => T extends X ? 1 : 2) extends
    <T>() => T extends Y ? 1 : 2 ? 
    	true
        : false

자세한 원리까지는 이해할 수 없지만, 함수의 반환 타입을 엄격하게 비교하는 동안에 정밀한 검사가 이뤄져 서로 완벽히 같은 타입인지를 판단하는 타입인 것으로 보인다.

내가 잘 설명할 수 있는 부분은 배열의 spreadinfer을 활용한 배열 판단이다.

T extends [infer Head,...infer Rest]

  • T가 빈 배열이거나 배열이 아닌 경우 false 조건이 만족되고, 나머지는 true조건이 만족된다.
  • T의 length가 1인 경우 Head에는 0번째 요소의 타입, Rest에는 빈 배열이 할당된다
  • T의 length가 2 이상인 경우 Head에는 0번째 요소의 타입, Rest에는 1번째 배열이후의 값들이(js의 array.slice(1)과 유사하다고 생각하면 쉽다) 할당된다

이를 통해 Head에 들어간 값이 U와 같으면 true를 반환하고, 그렇지 않다면 Includes<Rest,U>의 값을 확인한다.

빈 배열일 경우 무조건 false를 반환한다

타입 챌린지에서는 이와 같은 배열 순환 방식을 이해하면 대다수의 보통 난이도 문제를 풀 수 있게 된다.

0개의 댓글