๋ฐ˜์‘ํ˜•

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๋งต๋“œ ํƒ€์ž…(Mapped Types)์€ ๊ธฐ์กด ํƒ€์ž…์„ ์ƒˆ๋กœ์šด ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋ฌธ๋ฒ•์ด๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ map ๋ฐฐ์—ด ๋ฉ”์„œ๋“œ๋ฅผ ํƒ€์ž…์— ์ ์šฉํ•œ ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๋‹ค(๊ทธ๋ž˜์„œ ์ด๋ฆ„์ด Mapped Types๋‹ค).

 

๋งต๋“œ ํƒ€์ž… ๊ฐœ๋… ํ†บ์•„๋ณด๊ธฐ


์•„๋ž˜ ๋””๋ฐ”์ด์Šค ๋ธŒ๋žœ๋“œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์œ ๋‹ˆ์˜จ ํƒ€์ž… Devices๊ฐ€ ์žˆ๋‹ค. ์—ฌ๊ธฐ์— ๊ฐ ๋””๋ฐ”์ด์Šค ๋ธŒ๋žœ๋“œ์˜ ๊ฐ€๊ฒฉ ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  ์‹ถ์„ ๋•Œ ๋งต๋“œ ํƒ€์ž…์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

type Devices = 'APPLE' | 'OPPO' | 'XIAOMI'; // ๋””๋ฐ”์ด์Šค ๋ธŒ๋žœ๋“œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์œ ๋‹ˆ์˜จ ํƒ€์ž…
type DevicePrices = { [P in Devices]: number }; // ๋””๋ฐ”์ด์Šค ๋ธŒ๋žœ๋“œ ํƒ€์ž…์„ ์ˆœํšŒํ•ด์„œ key๋กœ ์ •์˜๋˜๋Š” ๋งต๋“œ ํƒ€์ž…

const deviceInfo: DevicePrices = {
  APPLE: 1_200_000, // ์–ธ๋”์Šค์ฝ”์–ด "_"๋ฅผ ์ด์šฉํ•ด ์ˆซ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” Numeric Seperators ๋ฌธ๋ฒ•
  OPPO: 800_000,
  XIAOMI: 700_000,
};

 

[P in Devices] ์ฝ”๋“œ๋Š” Devices ํƒ€์ž…์˜ ๊ฐ ๋ฌธ์ž์—ด์„ ์ˆœํšŒํ•œ ํ›„ number ํƒ€์ž…์„ ๊ฐ’์œผ๋กœ ๊ฐ€์ง€๋Š” ๊ฐ์ฒด์˜ ํ‚ค๋กœ ์ •์˜๋œ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ for in ๋ฌธ๋ฒ•๊ณผ ๋น„์Šทํ•˜๋‹ค. ํ‚ค ๋ถ€๋ถ„์— ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์ง์ ‘ ๋ช…์‹œํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

type DevicePrices = { [P in 'APPLE' | 'OPPO' | 'XIAOMI']: number };

 

๋งต๋“œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ๊ฐ ๋ธŒ๋žœ๋“œ๋งˆ๋‹ค number ํƒ€์ž…์„ ์ผ์ผ์ด ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

type DevicePrices = {
  APPLE: number;
  OPPO: number;
  XIAOMI: number;
};

 

๋งต๋“œ ํƒ€์ž… ์˜ˆ์ œ


์„œ๋ธŒ์…‹ ํƒ€์ž…

์•„๋ž˜ Subset ๋งต๋“œ ํƒ€์ž…์€ ์ธํ„ฐํŽ˜์ด์Šค ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ์ •์˜ํ•˜๋Š” ํƒ€์ž…์„ ๋ฐ›์•„, ํ•ด๋‹น ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ๋ถ€๋ถ„์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ค€๋‹ค(๋ถ€๋ถ„ ์ง‘ํ•ฉ์„ ๋งŒ์กฑํ•˜๋Š” ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜).

type Subset<T> = { [P in keyof T]?: T[P] };
// keyof T : T๋กœ ๋ฐ›์€ ๊ฐ์ฒด์˜ ๋ชจ๋“  ์†์„ฑ๋ช…์„ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์œผ๋กœ ๋‚˜์—ด
// [P in keyof T]?... : T ๊ฐ์ฒด์˜ ์†์„ฑ ๊ฐ’ ์ž์ฒด๋ฅผ ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ฆ. ๋ฌผ์Œํ‘œ๋ฅผ ๋ถ™์˜€์œผ๋ฏ€๋กœ ์„ ํƒ์  ์†์„ฑ

interface Student {
  name: string;
  age: number;
}

const ageOnly: Subset<Student> = { age: 18 }; // OK
const nameOnly: Subset<Student> = { name: 'Smith' }; // OK
const smith: Subset<Student> = { name: 'Smith', age: 18 }; // OK
const empty: Subset<Student> = {}; // OK (์„ ํƒ ์†์„ฑ์ด๋ฏ€๋กœ)

 

Boolean ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜

๊ธฐ์กด ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ชจ๋“  ์†์„ฑ์„ Boolean ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋งต๋“œ ํƒ€์ž…์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์œ„์—์„œ ์‚ดํŽด๋ดค๋˜ ์„œ๋ธŒ์…‹(๋ถ€๋ถ„ ์ง‘ํ•ฉ) ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋งต๋“œ ํƒ€์ž…๊ณผ ๊ฐ™์€ ์›๋ฆฌ๋‹ค.

type MakeBoolean<T> = { [P in keyof T]?: boolean };
// keyof T : T๋กœ ๋ฐ›์€ ๊ฐ์ฒด์˜ ๋ชจ๋“  ์†์„ฑ๋ช…์„ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์œผ๋กœ ๋‚˜์—ด
// [P in keyof T]?... : T์˜ ๋ชจ๋“  ์†์„ฑ ๊ฐ’์„ Boolean ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ฆ. ๋ฌผ์Œํ‘œ๋ฅผ ๋ถ™์˜€์œผ๋ฏ€๋กœ ์„ ํƒ์  ์†์„ฑ

interface Student {
  name: string;
  age: number;
}

const studentMap: MakeBoolean<Student> = {
  name: true, // OK
  age: undefined, // OK (์„ ํƒ ์†์„ฑ์ด๋ฏ€๋กœ)
};

studentMap.name = 3; // Error (boolean ํƒ€์ž…์ด ์•„๋‹ˆ๋ฏ€๋กœ)

 

์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…


ํƒ€์ž… ์žฌํ™œ์šฉ — Partial

Partial<Type>

 

๐Ÿ’ก Readonly, Partial, Required, Pick, Omit ๋“ฑ์€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์— ์ด๋ฏธ ๋‚ด์žฅ๋˜์–ด ์žˆ๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด๋‹ค. ๋” ๋งŽ์€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์€ ๊ณต์‹ ๋ฌธ์„œ ์ฐธ๊ณ .

 

์•„๋ž˜ ์‚ฌ์šฉ์ž์˜ ํ”„๋กœํ•„ ์ •๋ณด์— ์–ด๋–ค ํ•„๋“œ๊ฐ€ ์žˆ๋Š”์ง€ ์ •์˜๋˜์–ด ์žˆ๋Š” Profile ํƒ€์ž…์ด ์žˆ๋‹ค.

interface Profile {
  id: number;
  username: string;
  email: string;
}

 

๋งŒ์•ฝ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ •๋ณด๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค๋ฉด ๊ฐ ํ•„๋“œ๋ฅผ ๋ถ€๋ถ„์ ์œผ๋กœ ์ˆ˜์ •ํ•˜๋Š”๊ฒŒ ์ผ๋ฐ˜์ ์ด๋‹ค. ํ•˜์ง€๋งŒ ๊ธฐ์กด Profile ํƒ€์ž…์€ ๋ชจ๋“  ํ•„๋“œ๊ฐ€ ํ•„์ˆ˜ ์†์„ฑ์ด๋‹ค. ๋•Œ๋ฌธ์— Profile์˜ ๊ฐ ํ•„๋“œ๋ฅผ ์„ ํƒ์  ์†์„ฑ์œผ๋กœ ๊ฐ€์ง€๋Š” ํ”„๋กœํ•„ ์ •๋ณด ์—…๋ฐ์ดํŠธ ํƒ€์ž…์„ ๋”ฐ๋กœ ์ •์˜ํ•ด์•ผ ๋œ๋‹ค. ์ด๋•Œ ๋งต๋“œ ํƒ€์ž…์„ ์ด์šฉํ•˜๋ฉด ๊ธฐ์กด Profile ํƒ€์ž…์„ ์žฌํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

type ProfileUpdate = {
  [P in keyof Profile]?: Profile[P];
  // [P in 'id' | 'username' | 'email']?... ์™€ ๋™์ผ
};

 

๋งต๋“œ ํƒ€์ž…์œผ๋กœ ๊ตฌํ˜„๋œ ๋‚ด์žฅ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ธ Partial์„ ์ด์šฉํ•˜๋ฉด ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. Partial์€ ๋„˜๊ฒจ ๋ฐ›์€ ํƒ€์ž…์˜ ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ ํƒ์ (Optional)์œผ๋กœ ์„ค์ •ํ•œ ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค. ๋ฐ˜๋Œ€๋กœ Required๋Š” ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ๋ฅผ ํ•„์ˆ˜๋กœ ์„ค์ •ํ•œ ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค.

type ProfileUpdate = Partial<Profile>;
// Partial ๋‚ด๋ถ€ ๊ตฌํ˜„ : type Partial<T> = { [P in keyof T]?: T[P] };
// ProfileUpdate : { id?: number, username?: string, email?: string }

 

๋งŒ์•ฝ ๋งต๋“œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด Profile ํƒ€์ž…์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฐ ํ•„๋“œ๋ฅผ ์ค‘๋ณตํ•ด์„œ ์ž‘์„ฑํ•ด์•ผ ๋œ๋‹ค.

// ๋งต๋“œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๋•Œ 1
interface ProfileUpdate {
  id?: number;
  username?: string;
  email?: string;
}

// ๋งต๋“œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๋•Œ 2
type ProfileUpdate = {
  id?: Profile['id']; // number
  username?: Profile['username']; // string
  email?: Profile['email']; // string
};

 

์ฝ๊ธฐ์ „์šฉ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ — Readonly

Readonly<Type>

 

๋งต๋“œ ํƒ€์ž…์„ ์ด์šฉํ•ด ๊ธฐ์กด ํƒ€์ž…(T)์„ ์ฝ๊ธฐ ์ „์šฉ(Readonly)์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. Readonly ํƒ€์ž…์€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์— ์ด๋ฏธ ๋‚ด์žฅ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ(์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…) ๋”ฐ๋กœ ์ •์˜ํ•˜์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

interface Person {
  name: string;
  age: number;
}

type Readonly<T> = { readonly [P in keyof T]: T[P] }; // TS ๋‚ด์žฅ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…
type T1 = Readonly<Person>;
// T1 : { readonly name: string, readonly age: number }

 

๊ฐ์ฒด ํƒ€์ž…์—์„œ ์ผ๋ถ€ ์†์„ฑ๋งŒ ์„ ํƒ — Pick

Pick<Type, Keys>

 

๐Ÿ’ก extends ํ‚ค์›Œ๋“œ๋Š” ์ธํ„ฐํŽ˜์ด์Šค / ํƒ€์ž…์„ ํ™•์žฅํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด K extends T ์ฝ”๋“œ๋Š” ์ œ๋„ค๋ฆญ K๋ฅผ ์ œ๋„ค๋ฆญ T์˜ ํ•˜์œ„ ํƒ€์ž…์œผ๋กœ ์ธ์‹ํ•˜๋„๋ก ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ T๊ฐ€ ๊ฐ€์ง„ ์†์„ฑ์„ K๋„ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค.

 

๋งต๋“œ ํƒ€์ž…์„ ์ด์šฉํ•ด ํŠน์ • ํƒ€์ž…(T)์˜ ์ผ๋ถ€ ์†์„ฑ(K)๋งŒ ์„ ํƒํ•ด์„œ ์ƒˆ๋กœ์šด ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. Pick ํƒ€์ž… ์—ญ์‹œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์— ์ด๋ฏธ ๋‚ด์žฅ๋˜์–ด ์žˆ๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด๋‹ค.

interface Person {
  name: string;
  age: number;
  language: string;
}

type Pick<T, K extends keyof T> = { [P in K]: T[P] }; // TS ๋‚ด์žฅ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…
type T1 = Pick<Person, 'name' | 'age'>;
// T1 : {name: string, age: number}

 

์ œ๋„ค๋ฆญ T์— Person ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋„˜๊ธฐ๋ฏ€๋กœ keyof T๋Š” 'name' | 'age' | 'language' ๊ฐ€ ๋œ๋‹ค. ๋•Œ๋ฌธ์— ์ œ๋„ค๋ฆญ K์— ๋„˜๊ธฐ๋Š” ๊ฐ’์€ keyof T์˜ ์ผ๋ถ€ ํ˜น์€ ์ „๋ถ€์—ฌ์•ผ ํ•œ๋‹ค.

 

๐Ÿ’ก Pick ๊ณผ ๋ฐ˜๋Œ€๋กœ ํŠน์ • ํƒ€์ž…์˜ ์ผ๋ถ€ ์†์„ฑ๋งŒ ์ œ๊ฑฐํ•œ ํƒ€์ž…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋Š” Omit ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…๋„ ์žˆ๋‹ค. ์‚ฌ์šฉ๋ฒ•์€ Pick ๊ณผ ๋™์ผํ•˜๋‹ค.

 

์œ ๋‹ˆ์˜จ ํƒ€์ž…์—์„œ ํŠน์ • ํƒ€์ž…๋งŒ ์ถ”์ถœ — Extract

Extract<Type, Union>

 

Extract<T, U> ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์€ T ํƒ€์ž…์—์„œ U์— ๋ช…์‹œํ•œ ํƒ€์ž…์„ ์ถ”์ถœํ•œ๋‹ค. Extract๋Š” ์ฃผ๋กœ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์—์„œ ํŠน์ • ํƒ€์ž…์„ ์ถ”์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. U์— ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด ์—ฌ๋Ÿฌ ์†์„ฑ์„ ํ•œ ๋ฒˆ์— ์ถ”์ถœํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

type UserKey = 'name' | 'age' | 'address';
type UserName = Extract<UserKey, 'name'>; // 'name'
type UserNameAge = Extract<UserKey, 'name' | 'age'>; // 'name' | 'age'

 

๊ฐ์ฒด ํƒ€์ž…์—์„œ ํŠน์ • ์†์„ฑ ์ œ์™ธ — Omit

Omit<Type, Keys>

 

Omit<T, K> ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์€ T ํƒ€์ž…์—์„œ K์— ๋ช…์‹œํ•œ ์†์„ฑ์„ ์ œ๊ฑฐํ•œ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค. Omit์€ ์ฃผ๋กœ ๊ฐ์ฒด ํƒ€์ž…์—์„œ ํŠน์ • ์†์„ฑ์„ ์ œ์™ธํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. K์— ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด ์—ฌ๋Ÿฌ ์†์„ฑ์„ ์ œ๊ฑฐํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

type User = { name: string; age: number; address: string };

type UserWithoutAddress = Omit<User, 'address'>;
// { name: string; age: number; }

type UserName = Omit<User, 'age' | 'address'>;
// { name: string; }

 

์œ ๋‹ˆ์˜จ ํƒ€์ž…์—์„œ ํŠน์ • ํƒ€์ž… ์ œ์™ธ — Exclude

Exclude<Type, ExcludedUnion>

 

Exclude<T, U> ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์€ T ํƒ€์ž…์—์„œ U์— ๋ช…์‹œํ•œ ํƒ€์ž…์„ ์ œ๊ฑฐํ•œ ์ƒˆ๋กœ์šด ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค. Exclude์€ ์ฃผ๋กœ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์—์„œ ํŠน์ • ํƒ€์ž…์„ ์ œ์™ธํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. U์— ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด ์—ฌ๋Ÿฌ ์†์„ฑ์„ ์ œ๊ฑฐํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

type UserKey = 'name' | 'age' | 'address';
type UserKeyWithoutAddress = Exclude<UserKey, 'address'>; // 'name' | 'age'
type UserName = Exclude<UserKey, 'address' | 'age'>; // 'name'

 

์†์„ฑ๊ฐ’์„ ๋‹ค๋ฅธ ํƒ€์ž…์œผ๋กœ ์ง€์ • — Record

Record<Keys, Type>

 

๋งต๋“œ ํƒ€์ž…์„ ์ด์šฉํ•ด ํŠน์ • ์†์„ฑ(K)์˜ ๊ฐ’์ด ๋‹ค๋ฅธ ํƒ€์ž…(T)์œผ๋กœ ์ง€์ •๋œ ํƒ€์ž…์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. Record ํƒ€์ž… ์—ญ์‹œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์— ์ด๋ฏธ ๋‚ด์žฅ๋˜์–ด ์žˆ๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด๋‹ค. [P in K]: T ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด K ์†์„ฑ('Smith' | 'John')์œผ๋กœ ๊ตฌ์„ฑ๋œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ , K ์†์„ฑ ๊ฐ’์˜ ํƒ€์ž…์€ T(Person ํƒ€์ž…)๊ฐ€ ๋œ๋‹ค.

interface Person {
  name: string;
  age: number;
}

type Record<K extends keyof any, T> = { [P in K]: T }; // TS ๋‚ด์žฅ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…
type T1 = Record<'Smith' | 'John', Person>;
// T1 : {Smith: Person, John: Person} 
// Smith, John ์†์„ฑ์˜ ๊ฐ’์€ Person ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์กฐ์˜ ๊ฐ์ฒด์—ฌ์•ผ ํ•จ

 

๊ฐ์ฒด ์†์„ฑ์„ ์„ ํƒ์ ์œผ๋กœ ๋ณ€๊ฒฝ — Optional

Optional<Type, Keys>

 

Optional์€ T ํƒ€์ž…์—์„œ K ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“œ๋Š” ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด๋‹ค. ๋จผ์ €, โ‘ ์ œ๋„ค๋ฆญ์œผ๋กœ ๋ฐ›์€ K ํ”„๋กœํผํ‹ฐ๋ฅผ ์ œ์™ธํ•œ ํƒ€์ž…์„ ์ƒ์„ฑํ•˜๊ณ (Omit<T, K>), โ‘ก์ œ์™ธํ•˜์ง€ ์•Š์€ ๋‚˜๋จธ์ง€(Pick<T, K>) ํ”„๋กœํผํ‹ฐ๋ฅผ โ‘ข์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“  ํ›„(Partial) โ‘ฃ์ด ๋‘˜ ํƒ€์ž…์„ & ์—ฐ์‚ฐ์ž๋กœ ๋‹ค์‹œ ํ•ฉ์น˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„๋๋‹ค. Optional์€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„  ์ œ๊ณตํ•˜์ง€ ์•Š๋Š” ์ปค์Šคํ…€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ด๋‹ค.

type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
// K -> 'age'
// Omit<T, K> -> { name: string }
// Pick<T, K> -> { age: number }
// Partial<...> -> { age?: number }

type Person = { name: string, age: number };
type OnlyName = Optional<Person, 'age'> // { name: string, age?: number }

 

Enum ์ด๋„˜ ํ™œ์šฉ


๐Ÿ’ก ์ด๋„˜์€ ๋ณธ์งˆ์ ์œผ๋กœ ๊ฐ์ฒด์ด๋ฏ€๋กœ ๋Ÿฐํƒ€์ž„์—์„  ๊ฐ์ฒด๋กœ ๋™์ž‘ํ•œ๋‹ค. ๋ฐ˜๋ฉด, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ(ํƒ€์ž… ๋ฌธ๋งฅ)์—์„  ์ด๋„˜์˜ ๋ชจ๋“  ๋ฉค๋ฒ„ ๊ฐ’์„ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰ํ•œ๋‹ค.

 

์ด๋„˜์€ ๋ช…ํ™•ํ•œ ์˜๋ฏธ๋ฅผ ์ง€๋‹Œ ์ƒ์ˆ˜ ๊ฐ’์„ ๊ทธ๋ฃนํ™”ํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์•ˆ์ •์„ฑ์„ ๋†’์—ฌ์ค€๋‹ค. ๋˜ํ•œ ํƒ€์ž… / ๊ฐ’ ๋ชจ๋‘ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์„œ ํšจ์œจ์ ์ธ ์ฝ”๋“œ ์ž‘์„ฑ์— ์œ ์šฉํ•˜๋‹ค.

 

์‹ค์ˆ˜ ๋ฐฉ์ง€

์Šค๋งˆํŠธํฐ ๋ธŒ๋žœ๋“œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋‚˜์—ดํ•œ Brands ์ด๋„˜ ํƒ€์ž…์ด ์žˆ๊ณ , ์ด๋„˜ ํƒ€์ž…์— ์žˆ๋Š” ๋ชจ๋“  ๋ธŒ๋žœ๋“œ์˜ ๊ฐ€๊ฒฉ์„ (๊ฐ์ฒด ํ˜•์‹์œผ๋กœ)์ •์˜ํ•ด์•ผ ๋œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณธ๋‹ค.

enum Brands {
  APPLE,  // 0
  OPPO,   // 1
  XIAOMI, // 2
}

 

๊ฐ ๋ธŒ๋žœ๋“œ์˜ ๊ฐ€๊ฒฉ์„ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด Brands ์ด๋„˜ ํƒ€์ž…์„ ์ฐธ์กฐํ•˜์—ฌ BRAND_PRICE ๊ฐ์ฒด๋ฅผ ์ •์˜ํ–ˆ๋‹ค. ์‹ค์ˆ˜๋กœ ์ƒค์˜ค๋ฏธ ๋ธŒ๋žœ๋“œ๊ฐ€(Brands.XIAOMI) ๋ˆ„๋ฝ๋์ง€๋งŒ ๋”ฐ๋กœ ํƒ€์ž…์„ ์ง€์ •ํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋งŒ์•ฝ Brands ์ด๋„˜ ํƒ€์ž…์— ๋‚˜์—ด๋œ ๋ชจ๋“  ๋ธŒ๋žœ๋“œ์˜ ๊ฐ€๊ฒฉ์„ ๋ช…์‹œํ•˜๋„๋ก ๊ฐ•์ œํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด?

const BRAND_PRICE = {
  [Brands.APPLE]: 100_000_000,
  [Brands.OPPO]: 80_000_000,
  // Brands.XIAOMI ์†์„ฑ ๋ˆ„๋ฝ
};

 

BRAND_PRICE ๊ฐ์ฒด์˜ ํƒ€์ž…์„ [key in Brands]... ๋งต๋“œ ํƒ€์ž…์œผ๋กœ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋Ÿผ Brands ์ด๋„˜ ํƒ€์ž…์˜ ๋ชจ๋“  ์†์„ฑ์„ ์ •์˜ํ•ด์•ผ๋งŒ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ์—์„  Brands.XIAOMI ์†์„ฑ์ด ๋ˆ„๋ฝ๋์œผ๋ฏ€๋กœ ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด์ฒ˜๋Ÿผ ๋งต๋“œ ํƒ€์ž…์„ ์ด์šฉํ•˜๋ฉด ๊ฐ์ฒด์˜ ํŠน์ • ์†์„ฑ์ด ๋ˆ„๋ฝ๋˜๋Š” ์‹ค์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

// Error! XIAOMI ์†์„ฑ์ด ์—†์œผ๋ฏ€๋กœ ์—๋Ÿฌ ๋ฐœ์ƒ
const BRAND_PRICE: { [key in Brands]: number } = {
  [Brands.APPLE]: 100_000_000,
  [Brands.OPPO]: 80_000_000,
  // Brands.XIAOMI ์†์„ฑ ๋ˆ„๋ฝ
};

 

์ด๋„˜ ํŠน์ง•

์ด๋„˜ ๋ฉค๋ฒ„์— ๊ฐ’์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด 0๋ถ€ํ„ฐ ์ฆ๊ฐ€ํ•˜๋Š” ์ˆซ์ž๋ฅผ ์ž๋™์œผ๋กœ ํ• ๋‹นํ•œ๋‹ค. ์ˆซ์ž ๊ธฐ๋ฐ˜์˜ ์ด๋„˜์€ ๋Ÿฐํƒ€์ž„ ๋•Œ key / value ์ •๋ณด๋ฅผ ๋ชจ๋‘ ์ €์žฅํ•œ๋‹ค. ๋”ฐ๋ผ์„œ key / value(์ˆซ์ž)๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

enum Brands {
  APPLE,  // 0
  OPPO,   // 1
  XIAOMI, // 2
}

console.log(Brands); // { '0': 'APPLE', '1': 'OPPO', '2': 'XIAOMI', APPLE: 0, OPPO: 1, XIAOMI: 2 }

console.log(Object.keys(Brands)); // ['0', '1', '2', 'APPLE', 'OPPO', 'XIAOMI']
console.log(Object.values(Brands)); // ['APPLE', 'OPPO', 'XIAOMI', 0, 1, 2]

console.log(Brands[0]); // 'APPLE'
console.log(Brands.APPLE); // 0

 

์ด๋„˜ ๊ฐ ๋ฉค๋ฒ„์— ๋ฌธ์ž์—ด ๊ฐ’์„ ํ• ๋‹นํ•˜๋ฉด ๋Ÿฐํƒ€์ž„์—์„  ํ•ด๋‹น ๋ฌธ์ž์—ด ๊ฐ’๋งŒ ๊ฐ€์ง„๋‹ค. ์ˆซ์ž ์ด๋„˜์ฒ˜๋Ÿผ key / value ๊ฐ’์„ ์ด์ค‘์œผ๋กœ ์ €์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.

enum Brands {
  APPLE = '์• ํ”Œ',
  OPPO = '์˜คํฌ',
  XIAOMI = '์ƒค์˜ค๋ฏธ',
}

console.log(Brands); // { APPLE: '์• ํ”Œ', OPPO: '์˜คํฌ', XIAOMI: '์ƒค์˜ค๋ฏธ' }

console.log(Object.keys(Brands)); // ['APPLE', 'OPPO', 'XIAOMI']
console.log(Object.values(Brands)); // ['์• ํ”Œ', '์˜คํฌ', '์ƒค์˜ค๋ฏธ']

console.log(Brands.APPLE); // '์• ํ”Œ'
console.log(Brands[0]); // Error! TS7053: ...

 

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ(ํƒ€์ž… ๋ฌธ๋งฅ)์—์„œ ์ด๋„˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ชจ๋“  ๋ฉค๋ฒ„ ๊ฐ’์„ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์œผ๋กœ ์ทจ๊ธ‰ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ Record key ์Šฌ๋กฏ์— ์‚ฌ์šฉํ•œ Colors ํƒ€์ž…์€ '๋นจ๊ฐ•'|'ํŒŒ๋ž‘'|'๋…น์ƒ‰' ์ด ๋œ๋‹ค.

enum Colors {
  RED = '๋นจ๊ฐ•',
  BLUE = 'ํŒŒ๋ž‘',
  GREEN = '๋…น์ƒ‰',
}

type ColorType = Record<Colors, number>;
// { ๋นจ๊ฐ•: number, ํŒŒ๋ž‘: number, ๋…น์ƒ‰: number }

 

๋ณ€์ˆ˜, ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋“ฑ์˜ ํƒ€์ž…์„ ๋ฌธ์ž์—ด ์ด๋„˜์œผ๋กœ ์ง€์ •ํ•˜๋ฉด ํ•ด๋‹น ์ด๋„˜์˜ ๋ฉค๋ฒ„๋งŒ ํ—ˆ์šฉํ•œ๋‹ค. ์ฆ‰, ์ด๋„˜ ๋ฉค๋ฒ„์˜ ๊ฐ’์ด ๋ฌธ์ž์—ด์ผ ๋•Œ ์ด๋„˜ ๋ฉค๋ฒ„๊ฐ€ ์•„๋‹Œ ์ผ๋ฐ˜ ๋ฌธ์ž์—ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค(์ˆซ์žํ˜• ์ด๋„˜์—์„œ ์ผ๋ฐ˜ ์ˆซ์ž๋Š” ๊ฐ€๋Šฅ).

enum Colors {
  RED = '๋นจ๊ฐ•',
  BLUE = 'ํŒŒ๋ž‘',
  GREEN = '๋…น์ƒ‰',
}

// ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… : Colors.RED | Colors.BLUE | Colors.GREEN
function paintWall(color: Colors) {
  console.log(`Painting the wall with ${color} color.`);
}

paintWall(Colors.RED); // 'Painting the wall with ๋นจ๊ฐ• color.'
paintWall('RED'); // Error! TS2345: Argument of type 'RED' is not assignable ...

 

๋ฒˆ์™ธ — ๊ฐ์ฒด์˜ Key / Value๋กœ ๊ตฌ์„ฑ๋œ ํƒ€์ž… ๋งŒ๋“ค๊ธฐ


์•„๋ž˜ ๊ฐ ์ด๋ฆ„(fox, chick, ...)์— ํ•ด๋‹นํ•˜๋Š” ์ƒ‰์ƒ๊ฐ’์„ ๋‚˜ํƒ€๋‚ด๋Š” colors ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค.

const colors = {
  fox: '#FFC7CC',
  chick: '#FFD784',
  alien: '#B2E9E3',
  devil: '#E1BCEA',
} as const; // ๊ฐ์ฒด์— ํ• ๋‹น๋œ ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์œผ๋กœ ํ”„๋กœํผํ‹ฐ ํƒ€์ž… ์ถ”๋ก 

 

๋งต๋“œ ํƒ€์ž…๊ณผ keyof ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•ด ๊ฐ์ฒด์˜ value๋งŒ ์ถ”์ถœํ•ด์„œ ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๊ฐ์ฒด key๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ์—†์ด keyof typeof ๊ฐ์ฒด ์ฝ”๋“œ๋กœ ๋ฐ”๋กœ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

type ValueOf<T> = T[keyof T]; // T๋กœ ๋ฐ›์€ ๊ฐ์ฒด์˜ ๋ชจ๋“  ๊ฐ’์„ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์œผ๋กœ ๋‚˜์—ด
// key๋งŒ ์ถ”์ถœํ•ด์„œ ํƒ€์ž…์œผ๋กœ ์„ค์ • / Object.keys ๋ฐ˜ํ™˜๊ฐ’์— ์‚ฌ์šฉ
type ColorKey = keyof typeof colors; // 'fox' | 'chick' | 'alien' | 'devil'

// value๋งŒ ์ถ”์ถœํ•ด์„œ ํƒ€์ž…์œผ๋กœ ์„ค์ • / Object.values ๋ฐ˜ํ™˜๊ฐ’์— ์‚ฌ์šฉ
type ColorValue = ValueOf<typeof colors>; // '#FFC7CC' | '#FFD784' | '#B2E9E3' | '#E1BCEA'

// ์ถ”์ถœํ•œ key/value๋ฅผ tuple ํƒ€์ž…์œผ๋กœ ์„ค์ • / Object.entries ๋ฐ˜ํ™˜๊ฐ’์— ์‚ฌ์šฉ
type ColorEntries = Array<[ColorKey, ColorValue]>;

 

Object.keys|values๋Š” ํƒ€์ž… ์ถ”๋ก ์„ ํ†ตํ•ด ๋”ฐ๋กœ ํƒ€์ž…์„ ์ง€์ •ํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ๋ฐ˜๋ฉด ํƒ€์ž… ์ถ”๋ก ์ด ์ž˜ ์•ˆ๋˜๋Š” Object.entries๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค์–ด๋‘๊ณ  ์“ฐ๋ฉด ํŽธํ•˜๋‹ค.

// ์ฝ”๋“œ ์ฐธ๊ณ  StackOverFlow
type Entries<T> = { [K in keyof T]: [K, T[K]] }[keyof T][];
const colorEntries = Object.entries(colors) as Entries<typeof colors>;

 

Entries์— ๋„˜๊ธด ํƒ€์ž…์ด { a: number, b: string } ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. ๋จผ์ € ๊ฐ ํ‚ค์™€, ํ•ด๋‹น ํ‚ค์˜ ๊ฐ’์œผ๋กœ ๊ตฌ์„ฑ๋œ ํŠœํ”Œ์„ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ ๋งต๋“œ ํƒ€์ž…์œผ๋กœ ๊ตฌ์„ฑํ•œ๋‹ค.

// { [K in keyof T]: [K, T[K]] } ๋ถ€๋ถ„์—์„œ ์ƒ์„ฑํ•œ ํƒ€์ž…
{
  a: ['a', number];
  b: ['b', string];
}

 

์ƒ์„ฑํ•œ ๋งต๋“œ ํƒ€์ž…์„ ์ธ๋ฑ์‹ฑํ•˜์—ฌ ๊ฐ ํ‚ค์— ๋Œ€์‘ํ•˜๋Š” ํŠœํ”Œ ํƒ€์ž…์„ ์ฐธ์กฐํ•˜๊ณ , ๋ชจ๋“  ํ‚ค(a, b)์— ๋Œ€ํ•œ ํŠœํ”Œ์„ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์œผ๋กœ ๊ฒฐํ•ฉํ•œ๋‹ค.

// [keyof T] ๋ถ€๋ถ„์—์„œ ์ƒ์„ฑํ•œ ํƒ€์ž…
['a', number] | ['b', string];

 

๋งˆ์ง€๋ง‰์œผ๋กœ ์ด ์œ ๋‹ˆ์˜จ ํƒ€์ž…์˜ ์š”์†Œ๋“ค์„ ๋ชจ๋‘ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฐ์—ด ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค.

// [] ๋ถ€๋ถ„์—์„œ ์ƒ์„ฑํ•œ ํƒ€์ž…
(['a', number] | ['b', string])[]

 

๋ ˆํผ๋Ÿฐ์Šค


 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•