타입스크립트에서는 다른 일반적인 프로그래밍 언어에서는 찾을 수 없는, 타입을 변환하는 것이 가능하다.
별 모양의 타입을 별 모양의 일부분만 변환하는 transform도 가능하다.
utility 타입을 가져다가 쓸 수도 있지만, 정확하게 어떻게 이것이 가능한 것인지 먼저 알아본다.
Index Type
{
const obj = {
name: 'ellie'
}
obj.name;
obj['name']
}
이것처럼 인덱스를 기준으로 타입을 결정할 수 있다.
type Animal = {
name: string;
age: number;
gender: 'male' | 'female'
}
type Name = Animal['name'] // string
const text: Name = 12 //error
type Gender = Animal['gender'] // male | female
type Keys = keyof Animal; // 'name' | 'age' | 'gender' 문자열 유니언이 들어간다.
const key: Keys = 'name';
type Person = {
name: string;
gender: Animal['gender']
}
const person: Person = {
name: 'ellie',
gender: 'male'
}
index 타입을 이용하면 다른 타입에 있는 키에 접근해서 그 키의 value의 타입을 그대로 다시 선언할 수 있다.
Mapped Type
기존에 있는 타입들을 이용하면서 조금 다른 형태로 변환하는 것
type Video = {
title: string;
author: string;
}
type VideoOptional = {
title?: string;
author?: string;
}
type VideoReadonly = {
readonly title: string;
readonly author: string;
}
[1,2].map(item => item * item)
type Optional<T> = {
[P in keyof T]?: T[P] // for ...in과 동일하다.
// P키는 T 타입의 모든 키들 중 하나다.
}
type VideoOptional = Optional<Video>
한 번 정의해놓으면 재사용성이 높다. generic과 비슷하다.
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
const video = Readonly<Video> = {
title: 'hello',
author: 'ellie'
}
기존 타입에서 다른 타입으로 성질을 변화할 수 있다.
type Nullable<T> = {
[P in keyof T]: T[P] | null;
}
const video = Nullable<Video> = {
title: null,
author: null
}
type Proxy<T> = {
get(): T;
set(value: T): void;
}
type Proxify<T> = {
[P in keyof T]: Proxy<T[P]>
}
Proxify라는 타입은 전달되는 어떤 오브젝트를 빙글빙글 돌면서 타입을 Proxy라는 타입으로 한 단계 감싸는 역할을 한다.
Map 타입을 이용하면, 기존의 타입에서 다른 타입으로 변환해주는 것을 해줄 수 있다.
Conditional Type
조건이 맞으면 어떤 타입을 선택한다
type Check<T> = T extends string? boolen : number;
type Type = Check<string> // string은 string을 상속하기 때문에 boolean이 된다.
type TypeName<T> = T extends string
? 'string'
: T extends number
? 'number'
: T extends boolean
? 'boolean'
: T extends undefined
? 'undefined'
: T extends Function
? 'function'
: 'object';
type T0 = TypeName<string>; // string
type T1 = TypeName<'a'>; // string
type T2 = TypeName<() => void> // function
conditional type은 어떤 타입이 이런 타입이라면, 이 타입을 써야지 라고 조건적으로 타입을 결정할 수 있는 타입이다.
Utility Type
ReadOnly
type Todo = {
title: string;
description: string;
}
function display(todo: Readonly<Todo>) {
todo.title = 'java'; //error!
}
이렇게 가변성, 수정이 가능한 오브젝틀르 여기저기 전달하는 것은 굉장히 위험하다.
항상 불변성을 보장하는 것이 중요하다.
이렇게 많이 쓰는 타입들은 유틸리티 타입이라고 해서
이미 타입스크립트 개발자가 만들어 놓았다.
Partial Type
Partial 타입은 기존의 타입 중에서 부분적인 것만 타입을 허용하고 싶을 때 이용할 수 있다.
type Todo = {
title: string;
description: string;
label: string;
priority: 'high' | 'low';
}
function updateTodo(todo: Todo, fieldToUpdate: Partial<Todo>): Todo {
return {...todo, ...fieldToUpdate}
}
const todo: Todo = {
title: 'hello',
description: 'learn typescript!',
label: 'study',
priority: 'high'
}
const updated = updateTodo(todo, {priority: 'low'})
Pick Type
type Video = {
id: string;
title: string;
url: string;
data: string;
}
function getVideo(id: string): Video {
return {
id,
title: 'video',
url: 'https://...',
data: 'byte-data...'
}
}
function getVideoMetadata(id: string): Pick<Video, 'id' | 'title'> {
return {
id: string
}
}
- 기존의 타입에서 원하는 속성만 골라서 타입을 만들 수 있다.
- 재사용성을 높이려면...
type VideoMetadaga = Pick<Video, 'id' | 'title'>
- 이런식으로 선언해서 사용할 수 있다.
Pick 구현
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
- 어떤 타입을 전달받아오고, K 라는 것은 T 타입에 있는 키들을 상속한 아이들이다.
- 그래서 항상 Pick을 사용할 때 기존의 T에 있는 키를 써야지, 다른 키를 쓰면 에러가 발생한다.
- 전달된 K들에 한해서만 빙글빙글 돌면서 타입을 결정한다.
Omit Type
Pick과는 반대로 원하는 것을 빼버릴 수 있다.
type videoMetadata = Omit<Video, 'url' | 'data'>;
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Exclude<T, U> = T extends U ? never : T;
- any타입을 가지고 있는 key들을 가지고 오고, Pick을 이용하는데
- T 타입을 그대로 유지하면서, T에 있는 key들 중에 K를 제외한 아이들을 Pick하게 된다.
Record type
type PageInfo = {
title: string;
}
type Page = 'home' | 'about' | 'contact';
const nav: Record<Page, PageInfo> = {
home: {
title: 'Home',
},
aboute: {
title: 'About'
}
}
- Map과 비슷하게 하나와 하나를 연결하고 싶을 때 하나를 키를 쓰고 나머지를 다른 타입으로 묶고 싶을 때 유용하게 쓸 수 있다.
기타
Readonly나 Nullable, Record를 많이 쓴다.
type Product = 'cat' | 'dog';
type NewProduct = Capitalize<Product> // 'Cat' | 'Dog'
'Typescript' 카테고리의 다른 글
Type challenges - First of Array, Length of Tuple, Exclude (0) | 2022.03.23 |
---|---|
Typescript challenges - Readonly, Pick, Tuple of object (0) | 2022.03.19 |
[Typescript] Type vs Interface (0) | 2021.08.12 |
[Typescript] 기본 타입 알아보기 (0) | 2021.06.17 |