TS 3.4 · 3부. 인터페이스와 제네릭

유틸리티 타입

Partial · Pick · Omit · Record 로 있는 타입을 고쳐 씁니다.

예상 학습 시간 16분 실행 단추가 있는 예제는 고쳐서 실행해 볼 수 있습니다 난이도 중급
개념 설명

있는 타입을 고쳐 씁니다

User 를 적어 두었는데, 어떤 자리에서는 일부만 필요하고 어떤 자리에서는 전부 없어도 되는 형태가 필요합니다. 그때마다 비슷한 타입을 다시 적으면 하나가 바뀔 때 여럿을 고쳐야 합니다.

TypeScript 가 미리 만들어 둔 것들이 있습니다. 제네릭(3.3)으로 되어 있어 꺾쇠 안에 고칠 타입을 넣습니다.

이름하는 일
Partial<T>모든 속성을 없어도 되게 만듭니다
Required<T>반대로, 모두 있어야 하게 만듭니다
Readonly<T>모두 바꿀 수 없게 만듭니다
Pick<T, K>고른 속성만 남깁니다
Omit<T, K>고른 속성만 뺍니다
Record<K, V>정해진 열쇠마다 값을 갖는 객체를 만듭니다
최소 예제 · 편집 가능

일부만 바꾸기 — Partial

main.ts
interface User {
	name: string;
	age: number;
	email: string;
}

function update(user: User, changes: Partial<User>): User {
	return { ...user, ...changes };
}

const before: User = { name: "홍길동", age: 27, email: "a@b.c" };
const after = update(before, { age: 28 });

console.log(after.name, after.age, after.email);
출력
홍길동 28 a@b.c

changes 에는 바꿀 것만 넘깁니다. Partial<User>{ name?: string; age?: number; email?: string } 와 같기 때문입니다.

User 에 속성을 하나 더하면 Partial<User>저절로 따라옵니다. 이것이 다시 적지 않는 값입니다.

상세 사용법

고르고 빼기 — Pick 과 Omit

main.ts
interface User {
	name: string;
	age: number;
	password: string;
}

type PublicUser = Pick<User, "name" | "age">;
type Safe = Omit<User, "password">;

const a: PublicUser = { name: "홍길동", age: 27 };
const b: Safe = { name: "김철수", age: 31 };

console.log(a.name, b.age);
출력
홍길동 31

둘은 같은 결과를 다르게 적은 것입니다. 어느 쪽이 나은지는 무엇이 바뀔지에 달려 있습니다. 속성이 늘어날 때 새 속성을 내보내고 싶으면 Omit, 내보내지 않으려면 Pick 이 안전합니다.

상세 사용법

열쇠마다 값 — Record

main.ts
type Level = "low" | "mid" | "high";

const label: Record<Level, string> = {
	low: "낮음",
	mid: "보통",
	high: "높음"
};

console.log(label.low, label.high);
출력
낮음 높음

빠뜨리면 막습니다. 값이 늘었는데 표를 고치지 않는 일을 잡아 줍니다.

main.ts
type Level = "low" | "mid" | "high";

const label: Record<Level, string> = {
	low: "낮음",
	mid: "보통"
};
출력
3:7 TS2741 'high' 속성이 '{ low: string; mid: string; }' 형식에 없지만 'Record<Level, string>' 형식에서 필수입니다.
흔한 실수

처음에 자주 걸리는 것

속성 이름을 따옴표로 적습니다

Pick<User, name> 이 아니라 Pick<User, "name"> 입니다. 이름 자체가 리터럴 타입(1.4)이기 때문입니다.

Partial 을 남용하는 것

모든 것을 없어도 되게 만들면 확인할 것이 늘어납니다. 값이 없을 수 있다는 뜻이므로 쓰는 쪽에서 매번 다뤄야 합니다. 실제로 일부만 넘기는 자리에만 사용합니다.

Omit 은 없는 이름도 지나갑니다

Omit<User, "passwrod"> 처럼 오타를 적어도 오류가 아닙니다. 뺄 것이 없을 뿐입니다. Pick 은 없는 이름을 적으면 막습니다.

실습 문제

직접 해보기

1. 입력 폼 타입 난이도 중

id · name · email 을 담는 타입에서, 새로 만들 때 입력하는 폼용 타입(아이디 제외)을 만드세요.

interface User {
	id: number;
	name: string;
	email: string;
}

type UserForm = Omit<User, "id">;

const form: UserForm = { name: "홍길동", email: "a@b.c" };

console.log(form.name, form.email);
2. 상태별 안내 문구 난이도 중

"pending" | "done" | "failed" 마다 안내 문구를 갖는 표를 Record 로 만드세요.

type Status = "pending" | "done" | "failed";

const message: Record<Status, string> = {
	pending: "처리하고 있습니다",
	done: "끝났습니다",
	failed: "실패했습니다"
};

console.log(message.pending);
console.log(message.failed);
요약

이 단원의 정리

  • 있는 타입을 고쳐 새 타입을 만드는 것들이 미리 준비되어 있습니다.
  • Partial 은 모두 없어도 되게, Required 는 모두 있어야 하게 만듭니다.
  • Pick 은 고른 것만 남기고 Omit 은 고른 것만 뺍니다.
  • Record 는 정해진 열쇠마다 값을 갖게 하고, 빠뜨리면 막습니다.
  • 원본이 바뀌면 이것들도 저절로 따라옵니다.