타입스크립트 타입 조작 방법 / 인덱스트 액세스 타입, 맵드 타입, 조건부 타입 등

인덱스드 액세스 타입

객체, 배열, 튜플 타입에서 특정 프로퍼티 타입만 추출하는 방식입니다.

객체에서 프로퍼티 타입 추출

// 인터페이스 타입 정의 (객체 타입 정의 시 인터페이스 활용)
interface 인터페이스명 {
  title: string;
  content: string;
  author: {
    id: number;
    name: string;
  };
}

// 인터페이스 타입을 충족하는 객체 변수 생성
const 객체명: 인터페이스명 = {
  title: "제목",
  content: "본문",
  author: {
    id: 1,
    name: "하영"
  }
}

// 인덱스드 엑세스 타입으로 객체 매개변수 타입 정의
// author: { id: number; name: string; } 과 동일한 타입 정의 효과
function 함수명(author: 인터페이스명["author"]) { // 인터페이스명["author"]["id"] 형태로 중첩 사용도 가능
  console.log(`${author.id}, ${author.name}`);
}

// 함수 호출 시, 객체 내 특정 프로퍼티 타입으로 추론
함수명(객체명.author);

T[“프로퍼티Key(=인덱스)”] 형태로 특정 프로퍼티 타입만 추출할 수 있습니다.
프로퍼티 내 타입들이 변경되어도, 매개변수 타입 정의 코드 변경이 필요하지 않습니다.

배열에서 프로퍼티 타입 추출

// 타입 별칭 정의 (배열 타입 정의 시 타입 별칭 활용)
type 타입별칭명 = {
  title: string;
  content: string;
  author: {
    id: number;
    name: string;
  };
}[];

// 배열 타입을 충족하는 배열 변수 생성
// number, 0 등 어떤 숫자를 넣어도 배열 요소 타입 정상 추출
const 객체명: 타입별칭명[number] = {
  title: "제목",
  content: "본문",
  author: {
    id: 1,
    name: "하영"
  }
}

// 인덱스드 엑세스 타입으로 배열 매개변수 타입 정의
// author: { id: number; name: string; } 과 동일한 타입 정의 효과
function 함수명(author: 타입별칭명[number]["author"]) {
  console.log(`${author.id}, ${author.name}`);
}

// 함수 호출 시, 객체 내 특정 프로퍼티 타입으로 추론
함수명(객체명.author);

배열 요소 타입을 추출하고, 그 안의 특정 프로퍼티 타입만 추출하는 인덱스트 액세스 타입 예시입니다.

튜플에서 프로퍼티 타입 추출

type 튜플타입명 = [number, string, boolean];

// 튜플의 각 인덱스 요소 타입 추출
type num = 튜플타입명[0];
type str = 튜플타입명[1];
type bool = 튜플타입명[2];

// 전체 요소 유니온 타입 추출 (number | string | boolean)
type union = 튜플타입명[number];

인덱스드 액세스 타입으로 튜플 타입의 각 요소 타입을 추출할 수 있습니다.


keyof 연산자

// 인터페이스 생성하여 객체 타입 정의
interface 인터페이스명 {
  name: string;
  age: number;
}

const 객체명: 인터페이스명 {
  name: "하영",
  age: 29
}

// 객체 내 특정 프로퍼티 값 반환 함수
// key 값은 인터페이스 내 프로퍼티 키 값만 허용
// key: "name" | "age" 유니온 타입으로 추론 됨
function 함수명(객체명: 인터페이스명, key: keyof 인터페이스명) {
  return 객체명[key];
}

// 함수 호출 시 출력값 : 하영
함수명(객체명, "name");

특정 객체 타입에서 프로퍼티 키들을 스트링 리터럴 유니온 타입으로 추출하는 연산자입니다.
keyof 연산자는 인터페이스 등 타입에 사용 가능하고, 객체에는 사용이 불가합니다.

keyof, typeof 연산자 활용

// 객체 생성
const 객체명 = {
  name: "하영",
  age: 29
}

// 객체 프로퍼티를 갖는 타입 별칭 정의
type 타입별칭명 = typeof 객체명;

// 객체 내 특정 프로퍼티 값 반환 함수
// 객체 프로퍼티를 갖는 타입에서 프로퍼티 키 유니온 타입 추출
function 함수명(객체명: 타입별칭명, key: keyof typeof 객체명) {
  return 객체명[key];
}

keyof, typeof 연산자로 객체 타입에서 프로퍼티 키들을 유니온 타입으로 추출할 수 있습니다.


Mapped 타입 (맵드 타입)

기존 객체 타입 프로퍼티 키들을 순회하며, 새로운 객체 타입을 정의하는 문법입니다.

Mapped 타입 정의

// 인터페이스 정의
interface 인터페이스명 {
  id: number;
  name: string;
  age: number;
}

// 맵드 타입 정의
type 타입별칭명 = {
  // key : "id" | "name" | "age" 프로퍼티 사용 가능
  // ? : 맵드 타입이 정의하는 모든 프로퍼티가 선택적 프로퍼티
  // value : 인터페이스[프로퍼티키]에 해당하는 타입 값 사용 가능
  [key in "id" | "name" | "age"]?: 인터페이스명[key];
  또는
  [key in keyof 인터페이스명]?: 인터페이스명[key];
}

맵드 타입은 인터페이스에서 사용할 수 없고, 타입 별칭으로만 정의할 수 있습니다.


템플릿 리터럴 타입

type 타입별칭명1 = "red" | "black";
type 타입별칭명2 = "dog" | "cat";

// 템플릿 리터럴 타입 문법으로 두 타입을 조합하여,
// 문자열 리터럴 유니온 타입 정의
// 예시 : "red - dog" | "red - cat" | "black - dog" | "black - cat"
type 템플릿리터럴타입명 = `${타입별칭명1} - ${타입별칭명2}`;

스트링 리터럴 타입들을 기반으로, 특정 패턴 문자열만 포함하는 유니온 타입입니다.


조건부 타입

type A = {
  a: number;
}

type B = {
  a: number;
  b: number;
}

// 삼항연산자로 조건부 타입 정의
// 타입B는 타입A를 포함하여 상속받으므로, number 타입으로 정의됨
type 타입별칭명 = B extends A ? number : string;

조건부 타입은 조건에 따라 타입을 다르게 지정할 수 있는 타입입니다.
extends, ?, : 등을 사용하여 삼항 연산자처럼 동작합니다.

제네릭, 조건부 타입 활용

// 조건부 타입으로 제네릭 타입 정의
type 타입별칭명<T> = T extends number? string : number;

// 타입 매개변수 number 전달 시, string 타입으로 추론됨
let 변수명: 타입별칭명<number>;

// 타입 매개변수 string 전달 시, number 타입으로 추론됨
let 변수명: 타입별칭명<string>;

제네릭 타입 매개변수로 number가 들어오면 string 타입이 되는 조건부 타입 예시입니다.

제네릭, 조건부 타입, 함수 오버로드 시그니처 응용

// 함수 오버로드 시그니처 정의
// 매개변수가 string이면, 반환타입 string
// 매개변수가 없으면, 반환타입 undefined
function 함수명<T>(text: T): T extends string ? string : undefined;

// 함수 구현부
function 함수명<T>(text: any) {
  if (typeof text === "string" {
    return text.replaceAll(" ", "");
  } else {
    return undefined;
  }
}

// 함수 호출 시 매개변수 string
// 함수 반환타입 string 추론
let result = 함수명("hi hello");
retult.toUpperCase();

// 함수 호출 시 매개변수 undefined
// 함수 반환타입 undefined 추론
let result = removeSpaces(undefined);

함수 오버로드 시그니처 정의 시, 실제 구현부 매개변수 타입은 타입 추론에 영향을 주지 않습니다.

분산 조건부 타입

type 타입별칭명<T> = T extends number ? string : boolean;

// 유니온 분해 발생, string | boolean으로 추론됨
type Result = 타입별칭명<number | boolean>; 

조건부 타입에 제네릭 매개변수로 유니온 타입이 들어오면 분산이 발생합니다.

조건부 타입 분산 방지

type 타입별칭명<T> = [T] extends [number] ? string : boolean;

// number | boolean 유니온 타입은 number 타입이 아니므로, boolean으로 추론됨
type Result = 타입별칭명<number | boolean>; 

조건부 타입에서 extends 양 옆을 튜플로 감싸면, 유니온 타입 분산을 방지할 수 있습니다.

infer 키워드

// 함수 타입 정의
type 타입별칭명1 = () => string;

// 조건부 타입 정의
// 제네릭 타입 T가 함수 타입이면, 함수 반환 타입을 R로 추론
type 타입별칭명2<T> = T extends () => infer R ? R : never;

// 조건부 타입 제네릭에 함수 타입 전달
type 타입별칭명3 = 타입별칭명2<타입별칭명1>;

infer 키워드를 사용하면, 조건부 타입 내 타입 추론이 가능합니다.


유틸리티 타입

기존 타입을 변형하거나 조합할 수 있는 타입스트립트 내장 타입 조작 도구입니다.
유틸리티 타입 수는 20개 이상이므로, 공식 문서를 참고하면 좋습니다.

Partial, Required, Readonly, Pick, Omit, Record 타입은 맵드 타입 기반의 유틸리티 타입입니다.
Exclude, Extract, ReturnType 타입은 조건부 타입 기반의 유틸리티 타입입니다.

Partial

interface 인터페이스명 {
  title: string;
  content: string;
  tags: string[];
  thumbnailURL?: string;
}

// Partial 타입으로 신규 타입 생성
const 객체명: Partial<인터페이스명> = {
  title: "임시 게시글",
  content: "작성중"
}

모든 프로퍼티를 선택적으로 만드는 타입입니다.

Partial 타입 구현 예시

type Partial<T> = {
  [key in keyof T]?: T[key];
}

제네릭으로 전달받은 객체 타입 속성들을 맵드 타입으로 순회하며 새로운 타입을 생성합니다.

Required

interface 인터페이스명 {
  title: string;
  content: string;
  tags: string[];
  thumbnailURL?: string;
}

// Required 타입으로 신규 타입 생성
const 객체명: Required<인터페이스명> = {
  title: "임시 게시글",
  content: "작성중"
  // 모든 프로퍼티를 작성하지 않으면 타입 오류 발생
}

모든 프로퍼티를 필수로 만드는 타입입니다.

Required 타입 구현 예시

type Required<T> = {
  [key in keyof T]-?: T[key];
}

선택적 프로퍼티 키워드 (?) 를 제거하면, 필수 프로퍼티가 됩니다.

Readonly

interface 인터페이스명 {
  title: string;
  content: string;
  tags: string[];
  thumbnailURL?: string;
}

// Readonly 타입으로 신규 타입 생성
const 객체명: Readonly<인터페이스명> = {
  title: "게시글",
  content: "작성완료",
  tags: ["카테고리명"]
}

모든 프로퍼티를 읽기 전용으로 만들어 객체 수정을 방지하는 타입입니다.

Readonly 타입 구현 예시

type Readonly<T> = {
  readonly [key in keyof T]: T[key];
}

객체 내 모든 속성 앞에 readonly를 붙여서 읽기 전용 속성으로 만들 수 있습니다.

Pick<T, K>

interface 인터페이스명 {
  title: string;
  content: string;
  tags: string[];
  thumbnailURL?: string;
}

// Pick 타입으로 신규 타입 생성
const 객체명: Pick<인터페이스명, "title" | "content"> = {
  title: "예전 글",
  content: "작성완료"
}

기존 타입 T에서 일부 프로퍼티 K만 선택하여 새로운 타입을 만듭니다.

Pick 타입 구현 예시

type Pick<T, K extends keyof T> = {
  [key in K]: T[key];
}

맵드 타입으로 유니온 타입을 순회하며, 특정 속성들만 추출한 신규 타입을 만들 수 있습니다.
K extends keyof T로 정의했기 때문에 K는 반드시 T의 속성만 가져야 합니다.

Omit<T, K>

interface 인터페이스명 {
  title: string;
  content: string;
  tags: string[];
  thumbnailURL?: string;
}

// Omit 타입으로 신규 타입 생성
const 객체명: Omit<인터페이스명, "tags" | "thumbnailURL"> = {
  title: "예전 글",
  content: "작성완료"
}

기존 타입 T에서 일부 프로퍼티 K를 제거하여 새로운 타입을 만듭니다.

Omit 타입 구현 예시

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

Pick 타입, Exclude 타입을 응용하여 Omit 타입을 생성할 수 있습니다.

Record<K, V>

type 타입별칭명 = Record<"프로퍼티명1" | "프로퍼티명2" | "프로퍼티명3", {url: string; size: number}>; 

키 K, 값 V로 구성된 객체 타입을 생성하는 유틸리티 타입입니다.
인덱스 시그니처와 비슷하지만 키 타입을 더 구체적으로 제한할 수 있습니다.
위 예시로 아래 타입과 동일한 타입이 생성됩니다.

type 타입별칭명 = {
  프로퍼티명1: {
    url: string;
    size: number;
  },
  프로퍼티명2: {
    url: string;
    size: number;
  },
  프로퍼티명3: {
    url: string;
    size: number;
  }
}

Record 타입 구현 예시

type Record<K extends keyof any, V> = {
  [key in K]: V;
}

K는 객체 키로 사용할 수 있는 타입만 들어올 수 있도록 지정하였습니다.

Exclude<T, U>

// string 타입으로 추론됨
type 타입별칭명 = Exclude<string | boolean, boolean>

기존 타입 T에서 U에 해당하는 타입들을 제거한 유니온 타입을 반환합니다.

Exclude 타입 구현 예시

type Exclude<T, U> = T extends U ? never : T;

조건부 타입 변수에 유니온 타입을 전달하면 분산되어, 유니온 각 멤버가 개별적으로 검사됩니다.

Extract<T, U>

// boolean 타입으로 추론됨
type 타입별칭명 = Extract<string | boolean, boolean>

기존 타입 T에서 U와 겹치는 타입만 추출한 유니온 타입을 반환합니다.

Extract 타입 구현 예시

type Extract<T, U> = T extends U ? T : never;

never는 공집합이므로, 분산된 각 결과가 다시 유니온 타입으로 묶일 때 사라지게 됩니다.

ReturnType

function 함수명() {
  return "문자열 반환";
}

// string 타입으로 추론됨
type 타입별칭명 = ReturnType<typeof 함수명>;

함수 타입 T의 반환값 타입을 추출하는 유틸리티 타입입니다.

ReturnType 타입 구현 예시

type ReturnType<T extends (...args: any) => any> = T extends (...agrs: any) => infer R ? R : never;

제네릭으로 함수 타입만 들어올 수 있도록 제약을 걸고, infer R로 반환값 타입 추론이 가능하면 R 타입을 반환합니다.