이 글은 제가 타입챌린지를 하면서 해석한 내용을 적는 글입니다. 틀린 내용이 있으면 댓글 달아주시면 감사하겠습니다.
https://github.com/type-challenges/type-challenges/blob/main/questions/00898-easy-includes/README.md
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;
type Includes<T extends readonly any[], U> = T extends [infer F, ...infer O] ?
Equal<F, U> extends true ?
true: Includes<O, U> : false;
// test case
type cases = [
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>,
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,
Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>,
Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
Expect<Equal<Includes<[1], 1 | 2>, false>>,
Expect<Equal<Includes<[1 | 2], 1>, false>>,
Expect<Equal<Includes<[null], undefined>, false>>,
Expect<Equal<Includes<[undefined], null>, false>>,
]
type Includes : Includes라는 타입을 정의한다.
<T extends readonly any[], U> : any타입의 원소를 갖는 상수 배열을 extends 받는 T와 U를 정의한다.
T extends [infer F, ...infer O] ? : 만약 T가 첫 번째 원소인 infer F와 나머지 배열인 infer O로 나뉜다면
Equal<F, U> extends true ? : 첫 번째 원소 F와 U가 동일한지 확인하고 만약 true를 반환한다면
true : Includes<O, U> : false; : true를 반환하고, F와 U가 다르면 Include<O, U>를 실행하고, 그것도 아니라면 false를 반환한다.
여기서 Equal의 정의를 보면 코드 맨위 줄과 같다.
type Equal<X, Y> : X와 Y라는 제네릭 인자를 가진 Equal이라는 타입을 정의한다.
(<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false
여기서 고민을 조금 했다. extends X 가 T만 해당되는건지, 아니면 <T>() => T 가 전부 해당되는 건지... 그래서 지인 찬스를 사용했는데, extends X가 함수에 적용될거라면, 함수 바깥에 괄호가 씌어져 있어야 한다고 한다. (<T>() => T) extends X 와 같이 말이다. 결론적으로 extends X는 T를 꾸미는 것이었다!
T가 X와 같으면 1을 리턴하고 아니면 2를 리턴하는 함수가 T가 Y와 같으면 1을 리턴하고 아니면 2를 리턴하는 함수와 같다면 true를 아니라면 false를 리턴한다.
- 참고로 앞부분의 T와 뒷부분의 T는 별개의 T이므로 (<T>() => T extends X ? 1 : 2) extends (<Z>() => Z extends Y ? 1 : 2) ? true : false 같이 작성해도 상관 없다.
참고로 extends는 편의상 '같다'라고 해석한 것이지 절대 같다는 뜻이 아니다. X extends Y 하면 보통 X는 Y를 만족해야한다. 이런 느낌이다. ex) { a: 1, b: 2 } extends { a: 1 } 같은 느낌이다. { a: 1, b: 2 } 는 { a: 1 }를 가져야 한다. 라는 느낌 이랄까. 이해가 안되셨다면 죄송하다...ㅠㅜ
Equal에 대한 자세한 설명은 아래 블로그를 보자.
https://kscodebase.tistory.com/643
아니면 내가 써 놓은 글이 있는데... 내 뇌피셜이 잔뜩 들었지만, 초보자가 이해하기 쉬울거라고 생각한다. 아마도.
'Language > Typescript' 카테고리의 다른 글
타입챌린지 : 3057-Push (easy) (0) | 2023.03.06 |
---|---|
타입챌린지 : Equal - 번외 (0) | 2023.03.06 |
타입챌린지 : 533-Concat (easy) (0) | 2023.03.04 |
타입챌린지 : 268-If (easy) (0) | 2023.03.04 |
타입챌린지 : 189-Awaited (easy) (0) | 2023.03.03 |