[TS] Type-Challenges 스터디 15주차
![[TS] Type-Challenges 스터디 15주차](/_next/image?url=https%3A%2F%2Fvelog.velcdn.com%2Fimages%2Fhayou%2Fpost%2F9649fc65-4a0a-44ac-adcf-bc3cf78699c4%2Fimage.jpg&w=3840&q=75)
[medium] 9898. Appear Only Once
View on GitHub: https://tsch.js.org/9898
문제
Find the elements in the target array that appear only once. For example:input: [1,2,2,3,3,4,5,6,6,6],ouput: [1,4,5].
문제 설명
- 배열을 받아, 중복되지 않는 요소를 반환
시도 1 (정답)
접근 방식
- 배열을 순회하면서, 각 요소가 몇 번 나오는지 카운트
- 카운트가 1인 요소만 배열로 반환
코드
type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
? 1
: 2
? true
: false;
type IsUniqueInArray<
T extends any[],
E,
CountArr extends unknown[] = []
> = T extends [infer First, ...infer Rest]
? IsEqual<First, E> extends true
? IsUniqueInArray<Rest, E, [...CountArr, unknown]>
: IsUniqueInArray<Rest, E, CountArr>
: CountArr["length"] extends 1
? true
: false;
type FindEles<
T extends any[],
Original extends any[] = T,
Result extends any[] = []
> = T extends [infer First, ...infer Rest]
? IsUniqueInArray<Original, First> extends true
? FindEles<Rest, Original, [...Result, First]>
: FindEles<Rest, Original, Result>
: Result;
코드 설명
- 이번에도 역시나 general type을 정확히 비교하기 위해 기존에 사용한
isEqual함수를 사용 - 배열을 순회하며, 각 요소가 몇 번 나오는지 확인
- 카운트가 1인 요소만 배열로 반환
[medium] 9989. Count Element Number to Object
View on GitHub: https://tsch.js.org/9989
문제
With type CountElementNumberToObject, get the number of occurrences of every item from an array and return them in an object.
예시
type Simple1 = CountElementNumberToObject<[]>; // return {}
type Simple2 = CountElementNumberToObject<[1, 2, 3, 4, 5]>;
// return {
// 1: 1,
// 2: 1,
// 3: 1,
// 4: 1,
// 5: 1
// }
type Simple3 = CountElementNumberToObject<[1, 2, 3, 4, 5, [1, 2, 3]]>;
// return {
// 1: 2,
// 2: 2,
// 3: 2,
// 4: 1,
// 5: 1
// }
문제 설명
- 배열을 받아, 각 요소의 개수를 객체로 반환
시도 1
접근 방식
- 우선 배열을
flatten처리 - 이후 각 요소를 돌면서
result에 해당 요소가 존재하는지 확인 - 있을 경우에
value의 카운트만 증가, 없을 경우에는 새로운 레코드 추가
코드
type Flatten<T extends any[]> = T extends [infer First, ...infer Rest]
? First extends any[]
? [...Flatten<First>, ...Flatten<Rest>]
: [First, ...Flatten<Rest>]
: [];
type GetResult<T extends Record<string, unknown[]>> = {
[K in keyof T]: T[K]["length"];
};
type AddResult<
T extends PropertyKey,
Result extends Record<string, unknown[]>
> = {
[K in keyof Result]: K extends `${T}` ? [...Result[K], unknown] : Result[K];
} & (`${T}` extends keyof Result ? {} : { [K in `${T}`]: [unknown] });
type GetCountElement<
T extends any[],
Result extends Record<string, unknown[]> = {}
> = T extends [infer First, ...infer Rest]
? [First] extends [never]
? GetCountElement<Rest, Result>
: First extends PropertyKey
? GetCountElement<Rest, AddResult<First, Result>>
: GetCountElement<Rest, Result>
: GetResult<Result>;
type CountElementNumberToObject<T extends any[]> = GetCountElement<Flatten<T>>;
코드 설명
- 우선 배열을
flatten처리 never처리를 위해[First] extends [never]처리- 이후 각 요소를 돌면서
result에 해당 요소가 존재하는지 확인 - 있을 경우에
value의 카운트 추가(unknown[]), 없을 경우에는 새로운 레코드 추가 - 이후
GetResult타입을 통해 객체로 반환
실패 이유
- 객체 키의 타입을 넣어주기엔 키 값으로 들어갈 수 없는 타입들이 생겨서 분기처리가 너무 복잡해짐
시도 2 (답지 확인)
코드
type Flatten<T extends any[]> = T extends [infer First, ...infer Rest]
? First extends any[]
? [...Flatten<First>, ...Flatten<Rest>]
: [First, ...Flatten<Rest>]
: [];
type Count<T, R extends Record<string | number, any[]> = {}> = T extends [
infer F extends string | number,
...infer L
]
? F extends keyof R
? Count<L, Omit<R, F> & Record<F, [...R[F], 0]>>
: Count<L, R & Record<F, [0]>>
: {
[K in keyof R]: R[K]["length"];
};
type CountElementNumberToObject<T extends any[]> = [T] extends [[never]]
? {}
: Count<Flatten<T>>;
코드 설명
- 배열
flatten처리 infer를 활용해F를string | number타입으로 제한F가keyof R에 존재하는지 확인- 존재할 경우
Count<L, Omit<R, F> & Record<F, [...R[F], 0]>>처리- 기존 객체에서
F를 제외한 객체를 만들고,F를 키로 하고 값을[...R[F], 0]으로 하는 객체를 만들어 합침 (count 증가)
- 기존 객체에서
- 존재하지 않을 경우
Count<L, R & Record<F, [0]>>처리R & Record<F, [0]>는R에서F를 키로 하고, 값을[0]으로 하는 객체
T순회를 마치면[K in keyof R]: R[K]["length"]타입을 통해 객체로 반환
[medium] 10969. Integer
View on GitHub: https://tsch.js.org/10969
문제
Please complete type Integer<T>, type T inherits from number, if T is an integer return it, otherwise return never.
문제 설명
- 숫자를 받아, 정수일 경우 숫자를 반환, 소수일 경우
never반환
시도 1
접근 방식
- 우선 숫자인지 확인
- 숫자일 경우 템플릿 리터럴로 분리해버리기
- 분리한 템플릿 리터럴에서 소수점 이하 숫자가 0인지 확인
코드
type IsAllZero<T extends string> = T extends `${infer First}${infer Rest}`
? First extends "0"
? IsAllZero<Rest>
: false
: true;
type Integer<T> = [T] extends [number]
? number extends T
? never
: `${T}` extends `${infer _Int}.${infer Float}`
? IsAllZero<Float> extends true
? T
: never
: T
: never;
코드 설명
- 우선 숫자인지 확인 (
[T] extends [number],number extends T) - 숫자일 경우 템플릿 리터럴로 분리 (
${T}extends${infer _Int}.${infer Float}) - 분리한 템플릿 리터럴에서 소수점 이하 숫자가 0인지 확인 (
IsAllZero<Float> extends true) - 소수점 이하 숫자가 0일 경우 정수 반환, 아닐 경우
never반환
[medium] 16259. To Primitive
View on GitHub: https://tsch.js.org/16259
문제
Please complete type ToPrimitive<T>, type T inherits from object, convert T to a primitive type.
예시
type X = {
name: "Tom";
age: 30;
married: false;
addr: {
home: "123456";
phone: "13111111111";
};
};
type Expected = {
name: string;
age: number;
married: boolean;
addr: {
home: string;
phone: string;
};
};
문제 설명
- 객체를 받아, 객체의 모든 프로퍼티를 기본 타입으로 변환
시도 1 (더러운 정답)
접근 방식
- 객체의 프로퍼티를 돌면서 기본 타입으로 변환
- 배열/객체일 경우 재귀적으로 처리
코드
type InferType<T> = T extends string
? string
: T extends number
? number
: T extends boolean
? boolean
: T extends (...args: any[]) => any
? Function
: T extends any[]
? ChangeArray<T>
: T extends object
? { [K in keyof T]: InferType<T[K]> }
: T;
type ChangeArray<T extends any[]> = {
[K in keyof T]: InferType<T[K]>;
};
type ChangeObject<T> = T extends (...args: any[]) => any
? Function
: T extends any[]
? ChangeArray<T>
: T extends object
? { [K in keyof T]: InferType<T[K]> }
: T;
type ChangeValue<T> = T extends string
? string
: T extends number
? number
: T extends boolean
? boolean
: T;
type ToPrimitive<T extends object> = T extends [...infer R]
? InferType<R>
: {
[K in keyof T]: T[K] extends object
? ChangeObject<T[K]>
: InferType<T[K]>;
};
코드 설명
-
객체의 프로퍼티를 돌면서 기본 타입으로 변환
-
기본 타입일 primitive 타입으로 반환
-
배열/객체일 경우 재귀적으로 처리
-
그런데 중복된 로직이 많은 것 같아서 하나씩 다 걷어내봄
시도 2 (깔끔한 정답)
코드
type PrimitiveType<T> = T extends string
? string
: T extends number
? number
: T extends boolean
? boolean
: T extends (...args: any[]) => any
? Function
: T extends any[] | object
? { [K in keyof T]: PrimitiveType<T[K]> }
: T;
type ToPrimitive<T extends object> = { [K in keyof T]: PrimitiveType<T[K]> };
[medium] 17973. DeepMutable
View on GitHub: https://tsch.js.org/17973
문제
Please complete type DeepMutable<T>, type T inherits from object, convert T to a mutable object.
예시
type X = {
readonly a: () => 1;
readonly b: string;
readonly c: {
readonly d: boolean;
readonly e: {
readonly g: {
readonly h: {
readonly i: true;
readonly j: "s";
};
readonly k: "hello";
};
};
};
};
type Expected = {
a: () => 1;
b: string;
c: {
d: boolean;
e: {
g: {
h: {
i: true;
j: "s";
};
k: "hello";
};
};
};
};
문제 설명
- 객체를 받아, 객체의 모든 프로퍼티를 가변 타입(not readonly)으로 변환
시도 1
접근 방식
- 객체의 프로퍼티를 돌면서 가변 타입으로 변환
- 객체일 경우 재귀적으로 처리
코드
type isNotPureObject<T extends object> = T extends [...infer _]
? true
: T extends () => {}
? true
: false;
type DeepMutable<T extends object> = {
-readonly [K in keyof T]: T[K] extends object
? isNotPureObject<T[K]> extends false
? DeepMutable<T[K]>
: T[K]
: T[K];
};
코드 설명
- 객체의 프로퍼티를 순회하면서 가변 타입으로 변환
- 객체일 경우 재귀적으로 처리
- 배열 및 함수일 경우 확인해서 그대로 반환
[medium] 18142. All
View on GitHub: https://tsch.js.org/18142
문제
Returns true if all elements of the list are equal to the second parameter passed in, false if there are any mismatches.
예시
type Test1 = [1, 1, 1];
type Test2 = [1, 1, 2];
type Todo = All<Test1, 1>; // should be same as true
type Todo2 = All<Test2, 1>; // should be same as false
문제 설명
- 배열을 받아, 배열의 모든 요소가 두 번째 파라미터와 같은지 확인
시도 1
접근 방식
- 배열을 순회하면서 두 번째 파라미터와 같은지 확인
- 같지 않으면 false 반환
- 모두 같으면 true 반환
코드
type MyEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
? 1
: 2
? true
: false;
type All<T extends any[], U> = T extends [infer First, ...infer Rest]
? MyEqual<First, U> extends true
? All<Rest, U>
: false
: true;
코드 설명
- 배열을 순회하면서 두 번째 파라미터와 같은지 확인 (MyEqual 함수 사용)
- 같지 않으면 false 반환
- 모두 같으면 true 반환
comments
loading…