View on GitHub: https://tsch.js.org/4
T
에서 K
프로퍼티만 선택해 새로운 오브젝트 타입을 만드는 내장 제네릭 Pick<T, K>
을 사용하지 않고 구현하세요.
type MyPick<T, K extends keyof T> = { [P in K]: T[P] };
T
는 원본 타입K
는 T
의 프로퍼티 키 타입 (extends keyof T
를 활용해 T
의 프로퍼티 키 타입으로 제한)[P in K]
는 K
의 각 프로퍼티 키를 순회하며 새로운 타입을 생성T[P]
는 P
프로퍼티 키(key)의 값(value)을 가져옴
as
와extends
차이
as
: 타입 단언 or 매핑된 타입 변환extends
: 타입 제약(generic) or 조건부 타입(conditional type) 정의// as의 사용(1): 타입 단언
const value: unknown = "hello";
const length = (value as string).length; // 타입 단언
// as의 사용(2): 매핑된 타입 변환
type RenameKeys<T> = {
[K in keyof T as `new_${string & K}`]: T[K];
};
type Original = { id: number; name: string };
type Renamed = RenameKeys<Original>;
// 결과:
// {
// new_id: number;
// new_name: string;
// }
// extends의 사용(1): 타입 제약
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// extends의 사용(2): 조건부 타입 정의
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
[P in K]란 무엇인가?
[]
는 매핑된 타입에서 특정 키를 순회하며 새로운 타입을 생성할 때 사용(index 아님)
P
는 매핑된 키를 나타내는 변수
in
은 순회(iteration)를 나타내는 키워드로, K의 각 요소를 순회
K
는 순회할 키의 집합(K는 일반적으로 keyof T처럼 타입의 키 집합)
[P in K]
는 K에 포함된 키를 하나씩 순회하며 새로운 타입의 키를 정의
type MappedType = {
[P in K]: ValueType; // 매핑된 타입의 키와 값 정의
};
View on GitHub: https://tsch.js.org/7
T
에서 모든 프로퍼티를 읽기 전용으로 만드는 내장 제네릭 Readonly<T>
을 사용하지 않고 구현하세요.
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
};
[P in keyof T]
: T
의 모든 프로퍼티 키를 순회하며 새로운 타입을 생성readonly
: 프로퍼티를 읽기 전용으로 만듦T[P]
는 P
프로퍼티 키(key)의 값(value)을 가져옴'readonly' 키워드는 무엇인가?
Typescript
고유의 Type Modifier
// 객체 타입의 프로퍼티
interface User {
readonly id: number;
name: string;
}
const user: User = { id: 1, name: "Hayou" };
user.id = 2; // 오류: 'id'는 읽기 전용 속성입니다.
// 배열
const numbers: readonly number[] = [1, 2, 3];
numbers[0] = 4; // 오류: 읽기 전용 배열에서는 값을 변경할 수 없습니다.
numbers.push(4); // 오류: 읽기 전용 배열에서는 요소를 추가할 수 없습니다.
// 매핑된 타입
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
};
interface User {
id: number;
name: string;
}
type ReadonlyUser = MyReadonly<User>;
const user: ReadonlyUser = { id: 1, name: "Hayou" };
user.id = 2; // 오류: 'id'는 읽기 전용 속성입니다.
View on GitHub: https://tsch.js.org/11
배열(튜플)을 받아, 각 원소의 값을 key/value로 갖는 오브젝트 타입을 반환하는 타입을 구현하세요.
type TupleToObject<T extends readonly any[]> = { [P in T[number]]: P };
T
는 배열(튜플) 타입T[number]
는 배열 T
의 모든 요소 타입을 나타냄 (배열 T
의 인덱스에 있는 모든 타입을 집합으로 반환)[P in T[number]]
는 T[number]
의 각 원소를 순회하며 새로운 타입을 생성
[P in T]
가 안되는 이유
T
는 ['tesla', 'model 3', 'model X']
라는 배열의 "전체"를 가리킴type T = ["tesla", "model 3", "model X"];
매핑된 타입에서 키는 배열 전체가 될 수 없고, 반드시 키로 사용할 수 있는 타입이어야 함
반면, T[number]
는 배열 T
의 요소 타입만을 추출하여 배열의 모든 요소를 하나의 집합으로 표현
배열 T의 각 요소가 P로 순회될 수 있도록 만들어 줌
type T = ["tesla", "model 3", "model X"];
type TElement = T[number]; // 'tesla' | 'model 3' | 'model X'
View on GitHub: https://tsch.js.org/14
배열(튜플) T
를 받아 첫 원소의 타입을 반환하는 제네릭 First<T>
를 구현하세요.
type First<T extends any[]> = T extends [] ? never : T[0];
T extends []
는 배열 T
가 비어있는지 확인T[0]
는 배열 T
의 첫 번째 요소를 가져옴T extends [] ? never : T[0]
는 배열 T
가 비어있으면 never
를 반환하고, 비어있지 않으면 첫 번째 요소를 반환View on GitHub: https://tsch.js.org/18
배열(튜플)을 받아 길이를 반환하는 제네릭 Length<T>
를 구현하세요.
type Length<T extends readonly any[]> = T["length"];
T extends readonly any[]
는 튜플 T
가 읽기 전용 배열이라는 것을 확인T["length"]
는 튜플 T
의 길이를 반환왜 읽기 전용 배열이어야 하는가?
type Length<T extends any[]> = T["length"]; // readonly 제거
const tesla = ["tesla", "model 3", "model X", "model Y"]; // as const 제거
Expect<Equal<Length<typeof tesla>, 4>>
와 Expect<Equal<Length<typeof spaceX>, 5>>
가 false가 된다.const tesla = ['tesla', 'model 3', 'model X', 'model Y']
)은 기본적으로 수정 가능한 배열(string[])
로 간주됩니다. 즉, typeof tesla
의 타입은 string[]
이다.type TeslaType = string[]; // 배열의 요소 타입은 string, 길이는 고정되지 않음
type TeslaTypeAsConst = readonly ["tesla", "model 3", "model X", "model Y"]; // 배열의 요소 타입은 string, 길이는 4
Length<T>
는 배열 타입에서 length
프로퍼티를 참조한다. 하지만 배열이 단순히 string[]
로 추론되면, 배열의 길이에 대한 정보를 포함하지 않음.Length<typeof tesla>
는 정확히 4라는 값 타입이 아니라, number
타입으로 추론됨View on GitHub: https://tsch.js.org/43
T
에서 U
에 할당할 수 있는 타입을 제외하는 내장 제네릭 Exclude<T, U>
를 이를 사용하지 않고 구현하세요.
type MyExclude<T, U> = T extends U ? never : T;
T extends U
는 T
가 U
에 할당할 수 있는지(T
가 U
의 서브타입인지) 여부를 확인T
가 U
에 할당할 수 있으면 never
를 반환하고, 그렇지 않으면 T
를 반환유니온 타입('a' | 'b' | 'c')에서
T
가 동작하는 방식
T
는 유니온 타입의 각 구성 요소에 대해 개별적으로 판단 (이 예시에서는 extends
를 판단)