티스토리 뷰

🐰 유틸리티 타입

제네릭, 맵드 타입, 조건부 타입 등의 타입 조작 기능을 이용해 실무에서 자주 사용되는 타입을 미리 만들어 놓은 것

 

 

 

 


🐰 맵드 타입 기반의 유틸리티 타입

1. Partial<T>

partial
부분적인, 일부분의

특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환하는 타입

interface Post {
    title: string;
    tag: string[];
    content: string;
    thumbnailURL?: string;
}

const draft: Partial<Post> = {
    title: '제목 나중에 짓자',
    content: '초안...',
}

타입 Post는 thumbnailURL 프로퍼티를 제외한 다른 프로퍼티는 선택적 프로퍼티가 아니다.

하지만 변수 draft는 Partial<T> 유틸리티 타입을 이용해 모든 프로퍼티를 선택적 프로퍼티로 만들어 모든 프로퍼티를 가지고 있지 않아도 오류가 발생하지 않는다.

 

 

 

 

Partial<T> 직접 구현하기

// Partial 타입 직접 구현
type Partial<T> = {
    // 맵드 타입 이용
    // T의 모든 key 꺼내오기
    [key in keyof T]? : T[key]; // ?를 붙여 선택적 프로퍼티로 만들기
};
  1. 하나의 타입 변수 T를 사용하는 제네릭 타입을 만듬
  2. 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
    • key in keyof T 를 이용해 T의 프로퍼티를 하나씩 꺼내오기
  3. 선택적 프로퍼티로 만들기

 

 

 

 

 

 

 

 

 

2. Required<T>

required
필수의, 필수적인

특정 객체 타입의 모든 프로퍼티를 필수(선택적이지 않은) 프로퍼티로 바꿔주는 타입

interface Post {
    title: string;
    tag: string[];
    content: string;
    thumbnailURL?: string;
}

const withThumbnailPost: Required<Post> = {
    title: '한입 타스 후기',
    tag: ['ts'],
    content: '',
    // 선택적 프로퍼티인 thumbnailURL도 필수 프로퍼티가 됨
    // thumbnailURL: 'https://...', // -> ❌
}

thumbnailURL은 선택적 프로퍼티로 정의가 되어있지만 필수 프로퍼티로 사용하고자 할 때는 Required<T> 유틸리티 타입을 이용한다.

 

Required<Post>는 Post 타입의 모든 프로퍼티가 필수 프로퍼티로 변환된 객체이므로 thumbnailURL 프로퍼티를 생략하게 되면 오류가 발생한다.

 

 

 

 

Required<T> 타입 직접 구현하기

// Required 직접 구현
type Required<T> = {
	// 맵드 타입을 이용해 프로퍼티 가져오기
    // 모든 프로퍼티에서 '선택적'이라는 기능을 빼주기
    [key in keyof T]-?: T[key];
}
  1. 하나의 타입 변수 T를 사용하는 제네릭 타입을 만듬
  2. 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
    • key in keyof T 를 이용해 T의 프로퍼티를 하나씩 꺼내오기
  3. 모든 프로퍼티를 필수 프로퍼티로 만들기
    • 모든 프로퍼티에서 '선택적'이라는 기능을 제거
    • =>  -?  를 프로퍼티 이름 뒤에 붙임
더보기

 -?
? 가 붙어있는 선택적 프로퍼티가 있으면 ? 를 제거하라는 의미

 

 

 

 

 

 

 

 

3. Readonly<T>

readonly
읽기 전용

특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 바꿔주는 타입

interface Post {
    title: string;
    tag: string[];
    content: string;
    thumbnailURL?: string;
}

const readonlyPost: Readonly<Post> = {
    title: '보호된 게시글 입니다.',
    tag: [],
    content: '',
};

// 프로퍼티 수정 불가능
readonlyPost.content = '본문';  // ❌

Readonly<T> 유틸리티 타입은 T 타입의 모든 프로퍼티를 readonly(읽기 전용) 프로퍼티로 변환한다. 

따라서 점표기법을 이용해 특정 프로퍼티의 값을 수정하려고 하면 오류가 발생한다.

 

 

 

 

Readonly<T> 타입 직접 구현하기

// Readonly 직접 구현
type Readonly<T> = {
	// 맵드 타입으로 객체의 프로퍼티 가져오기
    // readonly를 앞에 붙여 읽기 전용으로 만들기
    readonly[key in keyof T]: T[key];
}
  1. 하나의 타입 변수 T를 사용하는 제네릭 타입을 만듬
  2. 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
    • key in keyof T 를 이용해 T의 프로퍼티를 하나씩 꺼내오기
  3. 모든 프로퍼티를 readonly(읽기 전용) 프로퍼티로 만들기
    • 프로퍼티 이름 앞에 readonly를 붙임

 

 

 

 

 

 

 

 

4. Pick<T, K>

객체 타입으로부터 특정 프로퍼티만 골라내는 타입

interface Post {
    title: string;
    tags: string[];
    content: string;
    thumbnailURL?: string;
}


const legacyPost: Pick<Post, 'title' | 'content'> = {
    title: '옛날 글',
    content: '옛날 컨텐츠',
}

 

Pick<T, K> 유틸리티 타입은 특정 객체 타입을 타입 변수 T에 받고 이 객체에 존재하는 특정 프로퍼티를 타입 변수 K에 받아 특정 프로퍼티 만을 골라낸다.

따라서 변수 legacyPost는 Post 타입으로부터 'title'과 'content' 프로퍼티만 가져온 객체 타입이 된다.

 

 

 

 

Pick<T, K> 타입 직접 구현하기

// Pick<T, K> 직접 만들기
type Pick<T, K extends keyof T> = { 
    // 타입 변수 K가 T의 key로만 이루어진 string literal 유니온 타입임을 보장해야 함
    // K extends 'title' | 'tags' | 'content' | 'thumbnailURL'
    // 'title' | 'content' extends 'title' | 'tags' | 'content' | 'thumbnailURL' 
    [key in K]: T[key];
}
  1. 두개의 타입 변수 T, K를 사용하는 제네릭 타입을 만듬
  2. 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
    • key in K 를 이용해 특정 프로퍼티를 하나씩 꺼내오기
  3. K가 T의 key로만 이루어진 string literal 유니온 타입임을 보장하기
    • K extends keyof T 로 제약하기

 

 

 

 

 

 

 

 

5. Omit<T, K>

omit
생략하다, 빼다

객체 타입으로부터 특정 프로퍼티를 제거하는 타입

interface Post {
    title: string;
    tags: string[];
    content: string;
    thumbnailURL?: string;
}


const noTitlePost: Omit<Post, 'title'> = {
    // Post 타입의 title 프로퍼티만 제거
    content: '',
    tags: [],
   thumbnailURL: '', 
};

Omit<T, K> 유틸리티 타입은 특정 객체 타입을 타입 변수 T에 받아 타입 변수 K에 받아온 프로퍼티를 제거한다.

 

변수 noTitlePost는 Omit을 이용해 Post 타입으로부터 title 프로퍼티를 제거한 타입으로 변수의 타입을 정의한다.

 

 

 

 

 

Omit<T, K> 직접 구현하기

// Omit 직접 구현
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
// T = Post, K = 'title'
// Pick<Post, Exclude<keyof Post, 'title'>>
// Pick<Post, Exclude<'title' | 'content' | 'tags' | 'thumbnailURL', 'title>>
// Pick<Post, 'content' | 'tags' | 'thumbnailURL'>
  1. 두개의 타입 변수 T, K를 사용하는 제네릭 타입을 만듬
  2. K에 제약을 추가
    • K extends keyof T로 K가 T의 key로만 이루어진 string literal 유니온 타입임을 보장
  3. Pick 타입을 이용해 완성
    • keyof T는 'title' | 'content' | 'tags' | 'thumbnailURL' 
    • => Pick<T, Exclude<keyof T, K>>Pick<Post, Exclude<'title' | 'content' | 'tags' | 'thumbnailURL' , 'title>> 
    • 이를 한번 더 변환하면 Pick<Post, 'content' | 'tags' | 'thumbnailURL'>

 

 

 

 

 

 

 

 

6. Record<K, V>

새로운 객체 타입을 정의할 때 인덱스 시그니쳐처럼 유연하지만 조금 더 제한적인 객체 타입을 정의하는 타입

(실무에서 자주 사용하는 타입)

 

type ThumnailLegacy = {
    large: {
        url: string;
    };
    medium: {
        url: string;
    };
    small: {
        url: string;
    };
};

// 타입 ThumbnailLegacy에 버젼이 계속 추가되고 똑같이 생긴 프로퍼티를 추가해야하는 경우
// Record 타입으로 객체 타입 정의
type Thumbnail = Record<'large' | 'medium' | 'small' , {url: string}>;
// 첫번째 타입 변수 => 객체의 프로퍼티 키
// 두번째 타입 변수 => 키의 value 타입
// type Thumbnail = {
//    large: {
//        url: string;
//    };
//    medium: {
//       url: string;
//    };
//    small: {
//        url: string;
//    };
//}

Record<K, V> 유틸리티 타입은 첫번째 타입 변수로는 객체의 프로퍼티 키를 받아오고 두번째 타입 변수로는 각 프로퍼티 밸류를 받아 객체 타입을 정의한다.

 

타입 Thumbnail은 large, medium, small 프로퍼티가 있는 객체 타입이 된다.

 

Thumbnail에 프로퍼티를 추가하는 경우 첫번째 타입 변수 유니온에 추가로 작성한다. 또 버젼별로 동일한 새로운 프로퍼티를 추가하는 경우에는 두번째 타입 변수에 추가한다.

// 프로퍼티를 추가하는 경우 유니온에 추가 -> watch 프로퍼티 추가
// 버전별로 새로운 프로퍼티를 추가하는 경우 두번째 타입 변수에 추가 -> size: number 추가
type Thumbnail = Record<'large' | 'medium' | 'small' | 'watch', {url: string; size: number}>;


type Thumbnail = {
 large: {
     url: string;
     size: number;
 };
 medium: {
     url: string;
     size: number;
 };
 small: {
     url: string;
     size: number;
 };
 watch: {
     url: string;
     size: number;
 };
}

 

 

 

 

 

Record<K, V> 직접 구현하기

// Record 구현
type Record<K extends keyof any, V> = { // K에 제약 걸기 -> K에 들어오는 타입은 객체 타입의 키를 추출한 유니온 타입
    [key in K]: V
}
  1. 두개의 타입 변수 T, K를 사용하는 제네릭 타입을 만듬
  2. 맵드 타입을 이용해 기존의 객체 타입을 다른 타입으로 변환
    • key in K 를 이용해 특정 프로퍼티를 하나씩 꺼내오기
  3. K가 T의 key로만 이루어진 string literal 유니온 타입임을 보장하기
    • K extends keyof T 로 제약하기

 

 

 

 


🔗 타입스크립트 강의

 

한 입 크기로 잘라먹는 타입스크립트(TypeScript) - 인프런 | 강의

문법을 넘어 동작 원리와 개념 이해까지 배워도 배워도 헷갈리는 타입스크립트 이제 제대로 배워보세요! 여러분을 타입스크립트 마법사🧙🏻‍♀️로 만들어드립니다., 프론트엔드의 피할 수

www.inflearn.com

 

728x90