[TS] Type-Challenges 스터디 7주차
![[TS] Type-Challenges 스터디 7주차](/_next/image?url=https%3A%2F%2Fvelog.velcdn.com%2Fimages%2Fhayou%2Fpost%2Fa3fa4d7e-34f5-4fc2-8758-c911f42ee5ad%2Fimage.jpg&w=3840&q=75)
[Medium] 645. Diff
문제
O & O1의 차이점인 객체를 가져옵니다
예시
import type { Equal, Expect } from "@type-challenges/utils";
type Foo = {
name: string;
age: string;
};
type Bar = {
name: string;
age: string;
gender: number;
};
type Coo = {
name: string;
gender: number;
};
type cases = [
Expect<Equal<Diff<Foo, Bar>, { gender: number }>>,
Expect<Equal<Diff<Bar, Foo>, { gender: number }>>,
Expect<Equal<Diff<Foo, Coo>, { age: string; gender: number }>>,
Expect<Equal<Diff<Coo, Foo>, { age: string; gender: number }>>,
];
시도 1
접근 방식
- A 객체의 키를 순회하며 B 객체에 extends가 안될 경우 빈 객체에 키-값 쌍 추가
- 마찬가지로 B 객체에 A 객체에 extends가 안될 경우 빈 객체에 키-값 쌍 추가
- 위 두 객체의 결과를 유니온한 뒤 객체로 리턴
코드
type GetDiff<A, B> = { [K in keyof A]: A[K] extends B ? A[K] : never };
type Diff<O, O1, DiffIntersection = GetDiff<O, O1> & GetDiff<O1, O>> = {
[K in keyof DiffIntersection]: DiffIntersection[K];
};
type example1 = Diff<Foo, Bar>;
// { name: never; age: never; gender: never; }
실패 이유
- value가 never가 되는건 속성을 없애지는 않아서
key: never로 처리됨
시도 2 (정답)
접근 방식
- 시도 1과 접근 방식은 동일, as(key-remapping)을 활용해 value가 아니라 key값을 never 처리하도록 변경
코드
type GetDiff<A, B> = { [K in keyof A as K extends keyof B ? never : K]: A[K] };
type Diff<O, O1, DiffIntersection = GetDiff<O, O1> & GetDiff<O1, O>> = {
[K in keyof DiffIntersection]: DiffIntersection[K];
};
코드 설명
GetDiff함수는 첫번째 인자의 키를 순회하며 두번째 인자에 포함되지 않는 키를never로 처리한 객체를 리턴DiffIntersection은GetDiff<A, B>와GetDiff<B, A>의 유니온 객체- 이후
DiffIntersection의 키를 순회하며 각 키의 값을 리턴
[Medium] 949. AnyOf
문제
Python의 any function을 타입 시스템으로 구현하세요
배열을 사용하고 배열의 요소가 참이면 true를 반환합니다. 배열이 비어 있으면 false를 반환합니다
예시
type Sample1 = AnyOf<[1, "", false, [], {}]>; // expected to be true.
type Sample2 = AnyOf<[0, "", false, [], {}]>; // expected to be false.
시도 1
접근 방식
- 빈 배열인지 확인한 후, 빈 배열이 아닐 경우 재귀를 이용해 각 요소를 순회하며 단 한 번이라도 false 값을 가질 경우 false 리턴
- false 값인지는 따로 객체를 만들어 확인
코드
type IsFalse = 0 | "" | false | [] | {} | undefined | null;
type AnyOf<T extends readonly any[]> = T extends []
? false
: T extends [infer First, ...infer Rest]
? First extends IsFalse
? AnyOf<Rest>
: true
: false;
실패 이유
IsFalse에 들어있는{}가 모든 객체를 false로 잡아버림
시도 2 (정답)
접근 방식
- 기존 방식에서,
isEmptyObject라는 타입을 만들어 빈 객체를 false로 처리하도록 변경
코드
type IsEmptyObject<T> = keyof T extends never ? true : false;
type IsFalse<T> = T extends 0 | "" | false | [] | undefined | null
? true
: T extends object
? IsEmptyObject<T>
: false;
type AnyOf<T extends readonly any[]> = T extends []
? false
: T extends [infer First, ...infer Rest]
? IsFalse<First> extends true
? AnyOf<Rest>
: true
: false;
코드 설명
IsEmptyObject는keyof T가never(키가 존재하지 않는 빈 객체)일 경우 true를 리턴하는 타입IsFalse는 비어있는 배열, 0, "", false, undefined, null, 그리고 빈 객체를 true로 처리하는 타입AnyOf는 빈 배열일 경우 false를 리턴- 비어있지 않을 경우 재귀를 이용해 각 요소를 순회하며 단 한 번이라도
IsFalse타입이 false 값을 가질 경우 false 리턴
[Medium] 1042. IsNever
문제
input type으로 T를 받는 IsNever type을 구현하세요. 만약 T의 유형이 never으로 확인되면 true를 반환하고 아니면 false를 반환합니다
예시
type A = IsNever<never>; // expected to be true
type B = IsNever<undefined>; // expected to be false
type C = IsNever<null>; // expected to be false
type D = IsNever<[]>; // expected to be false
type E = IsNever<number>; // expected to be false
시도
접근 방식
T가never일 경우true를 리턴하고 아니면false를 리턴
코드
type IsNever<T> = [T] extends [never] ? true : false;
코드 설명
[T] extends [never]은T가never일 경우true를 리턴하고 아니면false를 리턴(분배법칙 X)
[Medium] 1097. IsUnion
문제
T를 입력으로 받고, T가 Union 유형으로 확인되는지 여부를 반환하는 IsUnion을 구현하세요
예시
type case1 = IsUnion<string>; // false
type case2 = IsUnion<string | number>; // true
type case3 = IsUnion<[string | number]>; // false
시도 1
접근 방식
- 유니온 타입을 튜플로 변환 후, 튜플의 길이 확인
- 튜플의 길이가 1보다 크면 유니온 아닐까?
코드
type UnionToTuple<T, K = T> = [T] extends [never]
? []
: K extends K
? [K, ...UnionToTuple<Exclude<T, K>>]
: never;
type IsUnion<T> = UnionToTuple<T>["length"] extends [any] ? true : false;
type example = UnionToTuple<string | number | boolean>;
실패 이유
- 튜플이 아니라 퍼머넌트로 나옴
시도 2 (정답)
접근 방식
- 분배법칙을 활용해 각 요소에 접근
- 각 요소가 전체 타입(입력받은
T)과 같으면 false, 다르면 true
코드
type IsUnion<T, U = T> = [T] extends [never]
? false
: T extends U
? [U] extends [T]
? false
: true
: never;
코드 설명
[T] extends [never]은T가never인 경우 분기처리T extends U를 활용해 각 요소를 순회(항상 참인 조건, 분배법칙만을 위한 요소)- 만약 각 요소와 전체 타입(
U= 입력받은T)이 같다면 이는 유니온 타입이 아니기 때문에false를 반환 - 각 요소와 전체 타입이 다르다면 이는 유니온 타입이기 때문에
true를 반환
[Medium] 1130. ReplaceKeys
문제
Union type의 key를 대체하는 ReplaceKeys를 구현하세요. 만약 일부 유형에 해당 key가 존재하지 않는다면 대체하지 않습니다. 타입은 세 개의 인자를 받습니다.
예시
type NodeA = {
type: "A";
name: string;
flag: number;
};
type NodeB = {
type: "B";
id: number;
flag: number;
};
type NodeC = {
type: "C";
name: string;
flag: number;
};
시도 1
접근 방식
- 유니온 타입을 순회하며 각 요소에 대해 T를 순회하며 키 값이 존재하면 Y[T]를, 존재하지 않는다면 never를 넣어주는 방식
코드
type ReplaceKeys<U, T extends keyof any, Y> = U extends U
? T extends T
? T extends keyof U
? T extends keyof Y
? Y[T]를 추가한 U
: never
: Record<T, never>를 추가한 U
: never
: never;
실패 이유
Y[T]를 추가한 U,Record<T, never>를 추가한 U를 구현할 방법을 못찾음
시도 2 (답지 확인)
접근 방식
코드
type ReplaceKeys<U, T extends keyof any, Y> = {
[K in keyof U]: K extends T ? (K extends keyof Y ? Y[K] : never) : U[K];
};
코드 설명
T는 키값이므로extends keyof any로 선언K in keyof U는 유니온 타입으로 묶인 각 객체들의 키들을 모두 순회- 각 키에 대해
K extends T조건을 만족할 경우(T가 객체의 키 값일 경우), 다음 조건 확인 K가Y의 키 값일 경우,Y[K]를 반환, 아닐 경우never를 반환K가T의 키 값이 아닐 경우,U[K]를 반환
[Medium] 1367. Remove Index Signature
문제
객체 유형에서 인덱스 시그니처를 제외하는 RemoveIndexSignature<T>를 구현하세요
예시
type Foo = {
[key: string]: any;
foo(): void;
};
type A = RemoveIndexSignature<Foo>; // expected { foo(): void }
시도 1
접근 방식
- key-remapping으로 키 값이 [key: any] 형태를 걸러내면 되지 않을까?
코드
type RemoveIndexSignature<T> = {
[K in keyof T as K extends [key: keyof any] ? never : K]: T[K];
};
실패 이유
[key: keyof any]형태 걸러내기 실패
시도 2 (답지 확인)
접근 방식
코드
type RemoveIndexSignature<T, P = PropertyKey> = {
[K in keyof T as P extends K ? never : K extends P ? K : never]: T[K];
};
코드 설명
P는PropertyKey로 선언- key-remapping으로
P extends K조건을 만족하는 경우, 해당 키 값을 제거 P extends K조건을 만족한다는 것은K가P(string | number | symbol)의 슈퍼타입이라는 것- 즉,
K가string,number,symbol이면never을 반환하여 키를 삭제 - 이후
K extends P조건을 만족하는 경우(K가 P의 서브타입일 경우, 예를 들어'foo',123,symbol등),K를 반환하여 키를 유지
추가 질문
PropertyKey 타입이란 무엇인가?
keyof any와 같은 타입- 문자열, 숫자, 심볼 타입을 포함하는 유니온 타입
Index Signature란 무엇인가?
- 인덱스 시그니처란 객체의 키 값을 동적으로 정의하는 방법
- 해당 키가 인덱스 시그니처로 정의된 타입일 경우, 값의 타입 또한 동적으로 정의됨
- 참조: https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures
comments
loading…