# ๐Ÿ“„ Item11 ~ 15


# Item11. ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ์˜ ํ•œ๊ณ„ ์ธ์ง€ํ•˜๊ธฐ

  • ๋‹ค์Œ ๋‘ ์˜ˆ์ œ์˜ ์ฐจ์ด๋ฅผ ์•Œ์•„๋ณด์ž.
// example 1

interface Room {
  numDoors: number;
  ceilingHeightFt: number;
}

const r: Room = {
  numDoors: 1,
  ceilingHeightFt: 10,
  elephant: 'present',
}
// example 2

interface Room {
  numDoors: number;
  ceilingHeightFt: number;
}

const obj = {
  numDoors: 1,
  ceilingHeightFt: 10,
  elephant: 'present',
}

const r: Room = obj;
  • example 1์—์„œ r์€ ํƒ€์ž… ์ฒด์ปค๊ฐ€ ํ•ด๋‹น๋˜๋Š” ์†์„ฑ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒํ•œ๋‹ค.
  • example 2์—์„œ obj ํƒ€์ž…์€ { numDoors: number; ceilingHeightFt: number; elephant: string }์œผ๋กœ ์ถ”๋ก ๋˜์–ด obj ํƒ€์ž…์€ Room ํƒ€์ž…์˜ ๋ถ€๋ถ„ ์ง‘ํ•ฉ์„ ํฌํ•จํ•˜๋ฏ€๋กœ, Room์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋ฉฐ ํƒ€์ž… ์ฒด์ปค๋„ ํ†ต๊ณผํ•œ๋‹ค.
  • ์ฒซ ๋ฒˆ์งธ ์˜ˆ์‹œ์˜ ๊ฒฝ์šฐ, ๊ตฌ์กฐ์  ํƒ€์ž… ์‹œ์Šคํ…œ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ค‘์š”ํ•œ ์˜ค๋ฅ˜ ์ค‘ ํ•˜๋‚˜์ธ '์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ' ๊ณผ์ •์ด ์ˆ˜ํ–‰๋˜์–ด์„œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒํ–ˆ๋‹ค.
  • ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ๊ฐ€ ํ• ๋‹น ๊ฐ€๋Šฅ๊ฒ€์‚ฌ์™€๋Š” ๋ณ„๋„์˜ ๊ณผ์ •์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ์•„์•ผ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํƒ€์ž… ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ๊ฐœ๋…์„ ์ •ํ™•ํžˆ ์žก์„ ์ˆ˜ ์žˆ๋‹ค.
interface Options {
  title: string;
  darkMode?: boolean;
}

function createWindow(options: Options) {
  if (options.darkMode) {
    setDarkMode();
  }
}

createWindow({
  title: 'Spider Solitaire',
  darkmode: true,
})
  • ์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋Ÿฐํƒ€์ž„์— ์–ด๋–ค ์ข…๋ฅ˜์˜ ์˜ค๋ฅ˜๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ "๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์€ ์•Œ๋ ค์ง„ ์†์„ฑ๋งŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ 'Options' ํ˜•์‹์— 'darkmode'์ด(๊ฐ€) ์—†์Šต๋‹ˆ๋‹ค. 'darkMode'์„(๋ฅผ) ์“ฐ๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๊นŒ?"์™€ ๊ฐ™์€ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์ฒ˜๋Ÿผ ์˜๋„ํ•œ ๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.
  • Options ํƒ€์ž…์€ ๋ฒ”์œ„๊ฐ€ ๋งค์šฐ ๋„“์–ด ์ˆœ์ˆ˜ํ•œ ๊ตฌ์กฐ์  ํƒ€์ž… ์ฒด์ปค๋Š” ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์˜ค๋ฅ˜๋ฅผ ์ฐพ์•„๋‚ด์ง€ ๋ชปํ•œ๋‹ค.
// ๋‘˜๋‹ค string ํƒ€์ž…์ธ title ์†์„ฑ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์„œ ํ• ๋‹น๋ฌธ์€ ์ •์ƒ(๋‘ ๊ฐ€์ง€ ๋ชจ๋‘ ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์ด ์•„๋‹ˆ๋ฏ€๋กœ ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ๊ฐ€ ๋˜์ง€ ์•Š์Œ)

const o1: Options = document; // ์ •์ƒ
const o2: Options = new HTMLAnchorElement; // ์ •์ƒ
  • ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ํƒ€์ž… ์‹œ์Šคํ…œ์˜ ๊ตฌ์กฐ์  ๋ณธ์งˆ์„ ํ•ด์น˜์ง€ ์•Š์œผ๋ฉด์„œ๋„ ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์— ์•Œ ์ˆ˜ ์—†๋Š” ์†์„ฑ์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Œ์œผ๋กœ์จ ์˜ค๋ฅ˜๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.
// ์•„๋ž˜ ๊ตฌ๋ฌธ์€ ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์ด๋ฏ€๋กœ ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ๊ฐ€ ๋œ๋‹ค.

const o3: Options = { darkmode: true, title: 'Ski Free' }; // ~~~ 'Options' ํ˜•์‹์— 'darkmode'์ด(๊ฐ€) ์—†์Šต๋‹ˆ๋‹ค.
// ํƒ€์ž… ๋‹จ์–ธ๋ฌธ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ฐ€๊ธ‰์ ์ด๋ฉด ์„ ์–ธ๋ฌธ์„ ์ด์šฉํ•ด์•ผ ํ•œ๋‹ค.

const o4 = { darkmode: true, title: 'Ski Free' } as Options; // ์ •์ƒ
  • ๋งŒ์•ฝ ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ๋ฅผ ์›์น˜ ์•Š๋Š”๋‹ค๋ฉด, ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ถ”๊ฐ€์ ์ธ ์†์„ฑ์„ ์˜ˆ์ƒํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.
interface Options {
  [otherOptions: string]: unknown;
  darkMode?: boolean;
}

const o5: Options = { darkmode: true }; // ์ •์ƒ

# Item12. ํ•จ์ˆ˜ ํ‘œํ˜„์‹์— ํƒ€์ž… ์ ์šฉํ•˜๊ธฐ


# โž• ํ•จ์ˆ˜ ๋ฌธ์žฅ๊ณผ ํ•จ์ˆ˜ ํ‘œํ˜„์‹

  • ํ‘œํ˜„์‹(expressions)
    • ๊ฐ’ ํ•˜๋‚˜๋กœ ๊ท€๊ฒฐ๋˜๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ ์กฐ๊ฐ
    • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ ์ค‘ ๊ฐ’์ด ๋“ค์–ด๊ฐ€๋Š” ๊ณณ์ด๋ฉด ์–ด๋””์—๋‚˜ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฌธ์žฅ(statements)
    • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ฌธ์žฅ์€ ๊ฐ’์ด ๋“ค์–ด์™€์•ผ ํ•  ๊ณณ์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์—†๋‹ค.
    • ์ด ๋“ค์€ ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ๋„, ๋Œ€์ž…์—ฐ์‚ฐ์˜ ๊ฐ’์œผ๋กœ๋„, ์—ฐ์‚ฐ์ž์˜ ํ”ผ์—ฐ์‚ฐ์ž๋กœ๋„ ์‚ฌ์šฉ๋  ์ˆ˜ ์—†๋‹ค.
function rollDice1(sides) { ... } // ํ•จ์ˆ˜ ๋ฌธ์žฅ
const rollDice2 = function(sides) { ... } // ํ•จ์ˆ˜ ํ‘œํ˜„์‹
const rollDice3 = (sides) => { ... } // ํ•จ์ˆ˜ ํ‘œํ˜„์‹
  • ํ•จ์ˆ˜ ๋ฌธ์žฅ๊ณผ ํ•จ์ˆ˜ ํ‘œํ˜„์‹์˜ ์ฐจ์ด
    • 'ํ•จ์ˆ˜ ๋ฌธ์žฅ'์œผ๋กœ ์ •์˜๋œ ํ•จ์ˆ˜๋Š” ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฌธ์žฅ๋ณด๋‹ค ์•ž์— ์žˆ๊ฑด ๋’ค์— ์žˆ๊ฑด ์œ ํšจ๋ด„์œ„(ํ•จ์ˆ˜ ์œ ํšจ๋ฒ”์œ„)๋งŒ ๊ฐ™์œผ๋ฉด ๋ฌธ์ œ ์—†๋‹ค.
    • ํ•˜์ง€๋งŒ 'ํ•จ์ˆ˜ ํ‘œํ˜„์‹'์œผ๋กœ ์ •์˜๋œ ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฌธ์žฅ๋ณด๋‹ค ๋’ค์— ์žˆ์–ด์•ผ๋งŒ ํ•œ๋‹ค.
foo(); // ์ •์ƒ ๋™์ž‘

function foo() {
  alert('call foo');
}


bar(); // ๋น„์ •์ƒ ๋™์ž‘

const bar = function() {
  alert('call bar');
}

๐Ÿ“– Reference

  • https://velog.io/@fromzoo/%ED%91%9C%ED%98%84%EC%8B%9DExpression%EA%B3%BC-%EB%AC%B8%EC%9E%A5Statement

  • https://dimdim.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%95%A8%EC%88%98%EB%AC%B8%EC%9E%A5-%EA%B3%BC-%ED%95%A8%EC%88%98%ED%91%9C%ED%98%84%EC%8B%9D%EC%9D%98-%EC%B0%A8%EC%9D%B4


  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ํ•จ์ˆ˜ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    • ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ถ€ํ„ฐ ๋ฐ˜ํ™˜๊ฐ’๊นŒ์ง€ ์ „์ฒด๋ฅผ ํ•จ์ˆ˜ ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•˜์—ฌ ํ•จ์ˆ˜ ํ‘œํ˜„์‹์— ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
    • ๊ทธ๋ฆฌ๊ณ  ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ์˜ ๋ฐ˜๋ณต์„ ์ค„์ผ ์ˆ˜๋„ ์žˆ๋‹ค.
  • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ณตํ†ต ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ํƒ€์ž…์œผ๋กœ ์ œ๊ณตํ•˜๊ธฐ๋„ ํ•œ๋‹ค.
    • ์ง์ ‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“ ๋‹ค๋ฉด, ๊ณตํ†ต ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์œ„ํ•œ ํƒ€์ž… ์„ ์–ธ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  • ์˜ˆ์‹œ) ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ fetch ํ•จ์ˆ˜
declare function fetch(
  input: RequestInfo, init?: RequestInit
): Promise<Response>;

async function checkedFetch(input: RequestInfo, init?: RequestInit) {
  const response = await fetch(input, init);
    
  if (!response.ok) {
    // ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ๋‚ด์—์„œ ๊ฑฐ์ ˆ๋œ ํ”„๋กœ๋ฏธ์Šค๋กœ ๋ณ€ํ™˜
    throw new Error(`Request failed: ${response.status}`);
  }
    
  return response;
}
// ์œ„ ์ฝ”๋“œ์—์„œ ๋” ๊ฐ„๊ฒฐํ•ด์ง„ ๋ฒ„์ „
// 1. ํ•จ์ˆ˜ ๋ฌธ์ž์„ ํ•จ์ˆ˜ ํ‘œํ˜„์‹์œผ๋กœ ๊ต์ฒด
// 2. ํ•จ์ˆ˜ ์ „์ฒด์— ํƒ€์ž…(typeof fetch)์„ ์ ์šฉ => ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ input๊ณผ init์˜ ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์คŒ

const checkedFetch: typeof fetch = async (input, init) => {
  const response = await fetch(input, init);
    
  if (!response.ok) {
    // ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ๋‚ด์—์„œ ๊ฑฐ์ ˆ๋œ ํ”„๋กœ๋ฏธ์Šค๋กœ ๋ณ€ํ™˜
    throw new Error(`Request failed: ${response.status}`);
  }
    
  return response;
}
  • ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํƒ€์ž… ์„ ์–ธ์„ ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ•จ์ˆ˜ ํ‘œํ˜„์‹ ์ „์ฒด ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์ฝ”๋“œ๋„ ๊ฐ„๊ฒฐํ•˜๊ณ  ์•ˆ์ „ํ•˜๋‹ค.
    • ๋‹ค๋ฅธ ํ•จ์ˆ˜์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜์™€ ๋™์ผํ•œ ํƒ€์ž…์„ ๊ฐ€์ง€๋Š” ์ƒˆ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜, ๋™์ผํ•œ ํƒ€์ž… ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๊ฐ€์ง€๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํƒ€์ž…๊ณผ ๋ณ€ํ™˜ ํƒ€์ž…์„ ๋ฐ˜๋ณตํ•ด์„œ ์ž‘์„ฑํ•˜์ง€ ๋ง๊ณ  ํ•จ์ˆ˜ ์ „์ฒด์˜ ํƒ€์ž… ์„ ์–ธ์„ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค.

# Item13. ํƒ€์ž…๊ณผ ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ฐจ์ด์  ์•Œ๊ธฐ

# (1) ๊ณตํ†ต์ 

  • ๋ช…๋ช…๋œ ํƒ€์ž…์ผ ๋•Œ๋Š” ํƒ€์ž…, ์ธํ„ฐํŽ˜์ด์Šค ๋ชจ๋‘ ์ถ”๊ฐ€ ์†์„ฑ๊ณผ ํ•จ๊ป˜ ํ• ๋‹นํ•˜๋ฉด ์•„๋ž˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
type TUser = {
  name: string;
  age: number;
}

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

const wally: TUser = {
  name: 'wally',
  age: 28,
  zipCode: 12345, // ํ˜•์‹ 'TUser' ํ˜•์‹์— ํ• ๋‹นํ•  ์ˆ˜ ์—†๋‹ค. ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์€ ์•Œ๋ ค์ง„ ์†์„ฑ๋งŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ 'TUser' ํ˜•์‹์— 'zipCode'์ด(๊ฐ€) ์—†๋‹ค.
}
  • ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค, ํƒ€์ž… ๋ชจ๋‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
type TDict = { [key: string] = string };

interface IDict {
  [key: string]: string;
}
  • ํ•จ์ˆ˜ ํƒ€์ž…๋„ ์ธํ„ฐํŽ˜์ด์Šค, ํƒ€์ž… ๋ชจ๋‘ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
type TFn = (x: number) => string;

interface IFn {
  (x: number): string;
}

const toStrT: TFn = x => '' + x; // ์ •์ƒ
const toStrI: IFn = x => '' + x; // ์ •์ƒ
  • ํƒ€์ž… ๋ณ„์นญ๊ณผ ์ธํ„ฐํŽ˜์ด์Šค ๋ชจ๋‘ ์ œ๋„ค๋ฆญ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

  • ์ธํ„ฐํŽ˜์ด์Šค๋Š” ํƒ€์ž…์„, ํƒ€์ž…์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

    • ๋‹จ, ์ฃผ์˜ํ•  ์ ์€ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์œ ๋‹ˆ์˜จ ํƒ€์ž… ๊ฐ™์€ ๋ณต์žกํ•œ ํƒ€์ž…์„ ํ™•์žฅํ•˜์ง€๋Š” ๋ชปํ•œ๋‹ค.
    • ๋ณต์žกํ•œ ํƒ€์ž…์„ ํ™•์žฅํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ํƒ€์ž…๊ณผ &๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
interface IStateWithPop extends TState {
  population: number;
}

type TStateWithPop = IState & { population: number };
  • ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„(implements)ํ•  ๋•Œ๋Š” ํƒ€์ž…๊ณผ ์ธํ„ฐํŽ˜์ด์Šค ๋‘˜ ๋‹ค ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

# (2) ์ฐจ์ด์ 

  • ์œ ๋‹ˆ์˜จ ํƒ€์ž…์€ ์žˆ์ง€๋งŒ ์œ ๋‹ˆ์˜จ ์ธํ„ฐํŽ˜์ด์Šค๋ผ๋Š” ๊ฐœ๋…์€ ์—†๋‹ค.
  • ์ธํ„ฐํŽ˜์ด์Šค๋Š” ํƒ€์ž…์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์œ ๋‹ˆ์˜จ์€ ํ•  ์ˆ˜ ์—†๋‹ค.
    • ๊ทธ๋Ÿฐ๋ฐ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ํ™•์žฅํ•˜๋Š” ๊ฒŒ ํ•„์š”ํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค.
type Input = { ... };
type Output = { ... };

interface VariableMap {
  [name: string]: Input | Output;
}
  • ๋˜๋Š” ์œ ๋‹ˆ์˜จ ํƒ€์ž…์— name ์†์„ฑ์„ ๋ถ™์ธ ํƒ€์ž…์„ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋‹ค.
// ์ด ํƒ€์ž…์€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†๋‹ค.
// type ํ‚ค์›Œ๋“œ๋Š” ์œ ๋‹ˆ์˜จ์ด ๋  ์ˆ˜ ์žˆ๊ณ , ๋งคํ•‘๋œ ํƒ€์ž… ๋˜๋Š” ์กฐ๊ฑด๋ถ€ ํƒ€์ž… ๊ฐ™์€ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์— ํ™œ์šฉ๋˜๊ธฐ๋„ ํ•œ๋‹ค.

type NamedVariable = (Input | Output) & { name: string };
  • ํŠœํ”Œ๊ณผ ๋ฐฐ์—ด ํƒ€์ž…๋„ type ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•ด ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์ธํ„ฐํŽ˜์ด์Šค๋กœ๋„ ํŠœํ”Œ๊ณผ ๋น„์Šทํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์œผ๋‚˜ ํŠœํ”Œ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” concat ๊ฐ™์€ ๋ฉ”์†Œ๋“œ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
type Pair = [number, number];
type StringList = string[];
type NamedNums = [string, ...number[]];
  • ์ธํ„ฐํŽ˜์ด์Šค๋Š” ํƒ€์ž…์— ์—†๋Š” '๋ณด๊ฐ•(augment)' ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
    • ์•„๋ž˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด '์„ ์–ธ ๋ณ‘ํ•ฉ(declaration merging)'์„ ์ด์šฉํ•ด์„œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ณด๊ฐ•ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ํƒ€์ž… ์„ ์–ธ ํŒŒ์ผ์„ ์ž‘์„ฑํ•  ๋Œ€๋Š” ์„ ์–ธ ๋ณ‘ํ•ฉ์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฉฐ ํ‘œ์ค€์„ ๋”ฐ๋ผ์•ผ ํ•œ๋‹ค.
    • ํƒ€์ž… ์„ ์–ธ์—๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ฑ„์›Œ์•ผ ํ•˜๋Š” ๋นˆํ‹ˆ์ด ์žˆ์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋ฐ”๋กœ ์ด ์„ ์–ธ ๋ณ‘ํ•ฉ์ด ๊ทธ๋ ‡๋‹ค.
interface IState {
  name: string;
  captial: string;
}

interface IState {
  population: number;
}

const wyoming: IState {
  name: 'Wyoming',
  captial: 'Cheyenne',
  population: 500000,
}; // ์ •์ƒ
  • ๋ณ‘ํ•ฉ์€ ์„ ์–ธ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ผ๋ฐ˜์ ์ธ ์ฝ”๋“œ์—์„œ๋„ ์ง€์›๋˜๋ฏ€๋กœ ์–ธ์ œ ๋ณ‘ํ•ฉ์ด ๊ฐ€๋Šฅํ•œ์ง€ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.
    • ํƒ€์ž…์€ ๊ธฐ์กด ํƒ€์ž…์— ์ถ”๊ฐ€์ ์ธ ๋ณด๊ฐ•์ด ์—†๋Š” ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

# (3) ํƒ€์ž…๊ณผ ์ธํ„ฐํŽ˜์ด์Šค ์ค‘ ์–ด๋–ค ๊ฒƒ์„ ์จ์•ผํ• ๊นŒ

  • ๋ณต์žกํ•œ ํƒ€์ž…์ด๋ผ๋ฉด ๊ณ ๋ฏผํ•  ๊ฒƒ๋„ ์—†์ด ํƒ€์ž… ๋ณ„์นญ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • ํƒ€์ž…๊ณผ ์ธํ„ฐํŽ˜์ด์Šค, ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ชจ๋‘ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ๊ฐ์ฒด ํƒ€์ž…์ด๋ผ๋ฉด ์ผ๊ด€์„ฑ๊ณผ ๋ณด๊ฐ•์˜ ๊ด€์ ์—์„œ ๊ณ ๋ คํ•ด ๋ด์•ผ ํ•œ๋‹ค.
    • ์ผ๊ด€๋˜๊ฒŒ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ์ž‘์—…ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์ผ๊ด€๋˜๊ฒŒ ํƒ€์ž…์„ ์‚ฌ์šฉ ์ค‘์ด๋ผ๋ฉด ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • ์–ด๋–ค API์— ๋Œ€ํ•œ ํƒ€์ž… ์„ ์–ธ์„ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹๋‹ค.
    • API๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์‚ฌ์šฉ์ž๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ํ•„๋“œ๋ฅผ ๋ณ‘ํ•ฉํ•  ์ˆ˜ ์žˆ์–ด ์œ ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

# Item14. ํƒ€์ž… ์—ฐ์‚ฐ๊ณผ ์ œ๋„ค๋ฆญ ์‚ฌ์šฉ์œผ๋กœ ๋ฐ˜๋ณต ์ค„์ด๊ธฐ

  • DRY(Don't Repeat Yourself) ์›์น™์„ ํƒ€์ž…์—๋„ ์ตœ๋Œ€ํ•œ ์ ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ํƒ€์ž…์— ์ด๋ฆ„์„ ๋ถ™์—ฌ์„œ ๋ฐ˜๋ณต์„ ํ”ผํ•ด์•ผ ํ•œ๋‹ค.
    • extends๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ธํ„ฐํŽ˜์ด์Šค ํ•„๋“œ์˜ ๋ฐ˜๋ณต์„ ํ”ผํ•ด์•ผ ํ•œ๋‹ค.

# โญ๏ธ ์˜ˆ์‹œ1 - mapped type

// before

interface State { 
  userId: string;
  pageTitle: string;
  recentFiles: string[];
  pageContents: string;
}

interface TopNavState {
  userId: string;
  pageTitle: string;
  recentFiles: string[];
}
// 1) State ์ธ๋ฑ์‹ฑ

type TopNavState = {
  userId: State['userId'];
  pageTitle: State['pageTitle'];
  recentFiles: State['recentFiles'];
}
// 2) mapped type ์ ์šฉ

type TopNavState = {
  [k in 'userId' | 'pageTitle' | 'recentFiles']: State[k]
}
// 3) ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ธ Pick ์ ์šฉ

type TopNavState = Pick<State, 'userId' | 'pageTitle' | 'recentFiles'>;

# โญ๏ธ ์˜ˆ์‹œ2 - ํƒœ๊ทธ๋œ ์œ ๋‹ˆ์˜จ

interface SaveAction {
  type: 'save';
}

interface LoadAction {
  type: 'load';
}

type Action = SaveAction | LoadAction;
type ActionType = 'save' | 'load'; // ํƒ€์ž…์˜ ๋ฐ˜๋ณต!
// Action ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์ธ๋ฑ์‹ฑ
type ActionType = Action['type']; // ํƒ€์ž…์€ "save" | "load"

# โญ๏ธ ์˜ˆ์‹œ3 - keyof ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ

interface Options {
  width: number;
  height: number;
  color: string;
  label: string;
}

interface OptionsUpdate {
  width?: number;
  height?: number;
  color?: string;
  label?: string;
}

class UIWidget {
  constructor(init: Options) { ... }
  update(options: OptionsUpdate) { ... }
}
// ๋งคํ•‘๋œ ํƒ€์ž…๊ณผ keyof๋ฅผ ์‚ฌ์šฉ

type OptionsKeys = keyof Options; // "width" | "height" | "color" | "label"
type OptionsUpdate = { [k in keyof Options]?: Options[k] };
// ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ธ Partial ์ ์šฉ
type OptionsUpdate = Partial<Options>;

# โญ๏ธ ์˜ˆ์‹œ4 - ๊ฐ’์˜ ํ˜•ํƒœ์— ํ•ด๋‹นํ•˜๋Š” ํƒ€์ž… ์ •์˜

  • ๊ฐ’์œผ๋กœ๋ถ€ํ„ฐ ํƒ€์ž…์„ ๋งŒ๋“ค์–ด ๋‚ผ ๋•Œ๋Š” ์„ ์–ธ์˜ ์ˆœ์„œ์— ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.
    • ํƒ€์ž… ์ •์˜๋ฅผ ๋จผ์ €ํ•˜๊ณ  ๊ฐ’์ด ๊ทธ ํƒ€์ž…์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ์„ ์–ธํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    • ๊ทธ๋ ‡๊ฒŒ ํ•ด์•ผ ํƒ€์ž…์ด ๋” ๋ช…ํ™•ํ•ด์ง€๊ณ , ์˜ˆ์ƒํ•˜๊ธฐ ์–ด๋ ค์šด ํƒ€์ž… ๋ณ€๋™์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
const INIT_OPTIONS = {
  width: 640,
  height: 480,
  color: '#00FF00',
  label: 'VGA',
};

type Options = typeof INIT_OPTIONS;
  • ํ•จ์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜ ๊ฐ’์— ๋ช…๋ช…๋œ ํƒ€์ž… ๋งŒ๋“œ๋Š” ๊ฒฝ์šฐ
    • ์ด๋•Œ๋Š” ์กฐ๊ฑด๋ถ€ ํƒ€์ž…์ด ํ•„์š”ํ•˜๋‚˜ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋Š” ์ผ๋ฐ˜์  ํŒจํ„ด์˜ ์ œ๋„ค๋ฆญ ํƒ€์ž…์ด ์ •์˜๋˜์–ด ์žˆ๋‹ค.
    • ์ด๋Ÿฐ ๊ฒฝ์šฐ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…์ธ ReturnType์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
function getUserInfo(userId: string) {
  // ์ƒ๋žต
  return {
    userId,
    name,
    age,
    zipCode,
  };
}

type UserInfo = ReturnType<typeof getUserInfo>;
  • typeof๋ฅผ ์ ์šฉํ•  ๋•Œ๋Š” ์ ์šฉ ๋Œ€์ƒ์ด ๊ฐ’์ธ์ง€ ํƒ€์ž…์ธ์ง€ ์ •ํ™•ํžˆ ์•Œ๊ณ , ๊ตฌ๋ถ„ํ•ด์„œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

# โญ๏ธ ์˜ˆ์‹œ5 - ์ œ๋„ค๋ฆญ ํƒ€์ž… ์ œํ•œ

  • ์ œ๋„ค๋ฆญ ํƒ€์ž…์€ ํƒ€์ž…์„ ์œ„ํ•œ ํ•จ์ˆ˜์™€ ๊ฐ™๋‹ค.
    • ํƒ€์ž…์„ ๋ฐ˜๋ณตํ•˜๋Š” ๋Œ€์‹  ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ ํƒ€์ž…๋“ค ๊ฐ„์— ๋งคํ•‘์„ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    • ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์ œํ•œํ•˜๋ ค๋ฉด extends ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
interface Name {
  first: string;
  last: string;
}

type DancingDuo<T extends Name> = [T, T];

const couple1: DancingDuo<Name> = [
  { name: 'Fred', last: 'Astaire' },
  { name: 'Ginger', last: 'Rogers' },
]; // ์ •์ƒ

// '{ first: string; }'์€ Name์„ ํ™•์žฅํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
const couple2: DancingDuo<{first: string}> = [
  { first: 'Sonny' },
  { first: 'Cher' },
]; // 'Name' ํƒ€์ž…์— ํ•„์š”ํ•œ 'last' ์†์„ฑ์ด '{ first: string; }' ํƒ€์ž…์— ์—†๋‹ค.
  • Pick์„ ์ด์šฉํ•ด์„œ๋„ ๋™์ผํ•œ ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
// Pick ๊ตฌํ˜„ ๋ชจ์Šต

// K๋Š” ์‹ค์ œ๋กœ T์˜ ํ‚ค์˜ ๋ถ€๋ถ„ ์ง‘ํ•ฉ, ์ฆ‰ keyof T๊ฐ€ ๋˜์–ด์•ผ ํ•œ๋‹ค.
// ๋˜ํ•œ ํƒ€์ž…์ด ๊ฐ’์˜ ์ง‘ํ•ฉ์ด๋ผ๋Š” ๊ด€์ ์—์„œ ์ƒ๊ฐํ•˜๋ฉด extends๋ฅผ 'ํ™•์žฅ'์ด ์•„๋‹ˆ๋ผ '๋ถ€๋ถ„ ์ง‘ํ•ฉ'์ด๋ผ๋Š” ๊ฒƒ์œผ๋กœ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.
type Pick<T, K extends keyof T> = {
  [k in K]: T[k]
};
type FirstLast = Pick<Name, 'first' | 'last'>; // ์ •์ƒ
type FirstMiddle = Pick<Name, 'first' | 'middle'>; // '"middle"' ํ˜•์‹์€ '"first" | "last"' ํ˜•์‹์— ํ• ๋‹นํ•  ์ˆ˜ ์—†๋‹ค.


# Item15. ๋™์  ๋ฐ์ดํ„ฐ์— ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์‚ฌ์šฉํ•˜๊ธฐ

// ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์‚ฌ์šฉํ•œ ์˜ˆ์‹œ
type Rocket = { [property: string]: string };

const rocket: Rocket = {
  name: 'Falcon 9',
  variant: 'v1.0',
  thrust: '4,940 kN',
}; // ์ •์ƒ
  • ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜์˜ ๊ฐ ์˜๋ฏธ
๋ถ„๋ฅ˜ ์„ค๋ช…
ํ‚ค์˜ ์ด๋ฆ„ ํ‚ค์˜ ์œ„์น˜๋งŒ ํ‘œ์‹œํ•˜๋Š” ์šฉ๋„๋กœ ํƒ€์ž… ์ฒด์ปค์—์„œ๋Š” ์‚ฌ์šฉ ์•ˆ ํ•จ
ํ‚ค์˜ ํƒ€์ž… string ์ด๋‚˜ number ๋˜๋Š” symbol์˜ ์กฐํ•ฉ์ด์–ด์•ผ ํ•˜์ง€๋งŒ, ๋ณดํ†ต์€ string์„ ์‚ฌ์šฉํ•จ
๊ฐ’์˜ ํƒ€์ž… ์–ด๋–ค ๊ฒƒ์ด๋“  ๋  ์ˆ˜ ์žˆ์Œ
  • ๋™์  ๋ฐ์ดํ„ฐ์— ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์‚ฌ์šฉํ•˜๋Š” ์ •ํ™•ํ•œ ๋ฐฉ๋ฒ•
interface Rocket {
  name: string;
  variant: string;
  thrust_kN: number;
}

const falconHeavy: Rocket = {
  name: 'Falcon Heavy',
  variant: 'v1',
  thrust_kN: 15200
};

  • ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ๋™์  ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด CSV ํŒŒ์ผ์ฒ˜๋Ÿผ ํ—ค๋” ํ–‰์— ์—ด ์ด๋ฆ„์ด ์žˆ๊ณ , ๋ฐ์ดํ„ฐ ํ–‰์„ ์—ด ์ด๋ฆ„๊ณผ ๊ฐ’์œผ๋กœ ๋งคํ•‘ํ•˜๋Š” ๊ฐ์ฒด๋กœ ๋‚˜ํƒ€๋‚ด๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ๋‹ค.
    • ์ผ๋ฐ˜์ ์ธ ์ƒํ™ฉ์—์„œ ์—ด ์ด๋ฆ„์ด ๋ฌด์—‡์ธ์ง€ ์•Œ๊ธฐ ์–ด๋ ค์šธ ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์ด ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
function parseCSV(input: string): { [columnName: string]: string }[] {
  const lines = input.split('\n');
  const [ header, ...rows ] = lines;
  const headerColumns = header.split(',');
    
  return rows.map(rowStr => {
    const row: { [columnName: string ]: string } = {};
    
    rowStr.split(',').forEach((cell, i) => {
      row[headerColumns[i]] = cell;
    });
      
    return rows;
  });
}
  • ๋ฐ˜๋ฉด ์—ด ์ด๋ฆ„์„ ์•Œ๊ณ  ์žˆ๋Š” ํŠน์ •ํ•œ ์ƒํ™ฉ์ด๋ผ๋ฉด ๋ฏธ๋ฆฌ ์„ ์–ธํ•ด ๋‘” ํƒ€์ž…์œผ๋กœ ๋‹จ์–ธ๋ฌธ์„ ์‚ฌ์šฉํ•œ๋‹ค.
interface ProductRow {
  productId: string;
  name: string;
  price: string;
}

declare let csvData: string;

const products = parseCSV(csvData) as unknown as ProductRow[];

// ์„ ์–ธํ•ด ๋‘” ์—ด๋“ค์ด ๋Ÿฐํƒ€์ž„์— ์‹ค์ œ๋กœ ์ผ์น˜ํ•œ๋‹ค๋Š” ๋ณด์žฅ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ถ€๋ถ„์ด ๊ฑฑ์ •๋œ๋‹ค๋ฉด ๊ฐ’ ํƒ€์ž…์— undefined๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
function safeParseCSV(input: string): { [columnName: string]: string | undefined }[] {
  return parseCSV(input);
}

// ๋ชจ๋“  ์—ด์˜ undefined ์—ฌ๋ถ€๋ฅผ ์ฒดํฌํ•ด์•ผ ํ•œ๋‹ค.
const rows = parseCSV(csvData);

const prices: { [product: string]: number } = {};

for (const row of rows) {
  prices[row.productId] = Number(row.price);
}

const saveRows = safeParseCSV(csvData);

for (const row of safeRows) {
  prices[row.productId] = Number(row.price); // 'undefined' ํ˜•์‹์„ ์ธ๋ฑ์Šค ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
}

  • ์–ด๋–ค ํƒ€์ž…์— ๊ฐ€๋Šฅํ•œ ํ•„๋“œ๊ฐ€ ์ œํ•œ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ๋ผ๋ฉด ์ธ๋ฑ์Šค ์‹œ๊ทธ๋‹ˆ์ฒ˜๋กœ ๋ชจ๋ธ๋งํ•˜์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค.
interface Row1 {
  [column: string]: number
} // ๋„ˆ๋ฌด ๊ด‘๋ฒ”์œ„

interface Row2 {
  a?: number;
  b?: number;
  c?: number;
  d?: number;
} // ์ตœ์„ 
// 1) Record๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‚ค ํƒ€์ž…์— ์œ ์—ฐ์„ฑ ์ œ๊ณต

type Vec3D = Record<'x' | 'y' | 'z', number>;
// 2) mapped type

type Vec3D = { [k in 'x' | 'y' | 'z']: number };
// ์‘์šฉ) ์กฐ๊ฑด๋ถ€ ํƒ€์ž…๊ณผ ์—ฐ๊ฒฐ

type ABC = { [k in 'a' | 'b' | 'c']: k extends 'b' ? string : number };
// Type ABC = {
//   a: number;
//   b: string;
//   c: number;
// }