# ๐Ÿ“„ Item19 ~ 23


# Item19. ์ถ”๋ก  ๊ฐ€๋Šฅํ•œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด ์žฅํ™ฉํ•œ ์ฝ”๋“œ ๋ฐฉ์ง€ํ•˜๊ธฐ

# (1) ํƒ€์ž… ์ถ”๋ก  ์žฅ์ 

  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ฒ˜์Œ ์ ‘ํ•œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ํฌํŒ…ํ•  ๋•Œ ๊ฐ€์žฅ ๋จผ์ € ํ•˜๋Š” ์ผ์€ ํƒ€์ž… ๊ตฌ๋ฌธ์„ ๋„ฃ๋Š” ๊ฒƒ์ด๋‹ค.
    • ๋ชจ๋“  ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•  ๋•Œ๋งˆ๋‹ค ํƒ€์ž…์„ ๋ช…์‹œํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๋งŽ์€ ํƒ€์ž… ๊ตฌ๋ฌธ์€ ์‚ฌ์‹ค ๋ถˆํ•„์š”ํ•˜๋‹ค.
    • ํƒ€์ž…์ด ์ถ”๋ก ์ด ๋œ๋‹ค๋ฉด ๋ช…์‹œ์  ํƒ€์ž… ๊ตฌ๋ฌธ์€ ํ•„์š”ํ•˜์ง€ ์•Š๋Š”๋‹ค.
let x: number = 12;
let x = 12; // ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ํƒ€์ž…์ด number๋กœ ์ด๋ฏธ ์ถ”๋ก ๋จ
  • ํƒ€์ž… ์ถ”๋ก ๋˜๋ฉด ๋ฆฌํŒฉํ† ๋ง ์—ญ์‹œ ์šฉ์ดํ•ด์ง„๋‹ค.
interface Product {
  id: string;
  name: string;
  price: number;
}

function logProduct(product: Product) {
  const id: number = product.id; // ๋งŒ์•ฝ ์ด์™€ ๊ฐ™์ด ์ž‘์„ฑํ•˜๋ฉด ํƒ€์ž… ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค.
  const name: string = product.name;
  const price: number = product.price;
  console.log(id, name, price);
}
  • ์ฐธ๊ณ ๋กœ ์œ„ ์ฝ”๋“œ ๋ณด๋‹ค๋Š” ๋น„๊ตฌ์กฐํ™” ํ• ๋‹น๋ฌธ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹๋‹ค.
function logProduct(product: Product) {
  const { id, name, price } = product;
  console.log(id, name, price);
}

# (2) ๋ฌด์กฐ๊ฑด ๋ชจ๋“  ๋ณ€์ˆ˜์— ํƒ€์ž…์„ ์ •์˜ํ•˜์ง€ ๋ง์ž

  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์ตœ์ข… ์‚ฌ์šฉ์ฒ˜๊นŒ์ง€ ๊ณ ๋ คํ•˜์—ฌ ํƒ€์ž…์„ ์ถ”๋ก ํ•˜์ง€ ์•Š๋Š”๋‹ค.

    • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ณ€์ˆ˜์˜ ํƒ€์ž…์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ฒ˜์Œ ๋“ฑ์žฅํ•  ๋•Œ ๊ฒฐ์ •๋œ๋‹ค.
  • ์ด์ƒ์ ์ธ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋Š” ํ•จ์ˆ˜/๋ฉ”์„œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜์— ํƒ€์ž… ๊ตฌ๋ฌธ์„ ํฌํ•จํ•˜์ง€๋งŒ, ํ•จ์ˆ˜ ๋‚ด์—์„œ ์ƒ์„ฑ๋œ ์ง€์—ญ ๋ณ€์ˆ˜์—๋Š” ํƒ€์ž… ๊ตฌ๋ฌธ์„ ๋„ฃ์ง€ ์•Š๋Š”๋‹ค.

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

// Don't!
app.get('/health', (request: express.Request, response: express.Response) => {
  response.send('OK');
})

// Do!
app.get('/health', (request, response) => {
  response.send('OK');
})

# (3) ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด ์ •์˜ํ•  ๋•Œ ํƒ€์ž… ์ถ”๋ก 

  • ํƒ€์ž…์ด ์ถ”๋ก ๋  ์ˆ˜ ์žˆ์Œ์—๋„ ์—ฌ์ „ํžˆ ํƒ€์ž…์„ ๋ช…์‹œํ•˜๊ณ  ์‹ถ์€ ์ƒํ™ฉ์ด ์กด์žฌํ•œ๋‹ค. ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด์„ ์ •์˜ํ•  ๋•Œ๋ฅผ ์‚ดํŽด๋ณด์ž.
const elmo: Product = {
  name: 'Elmo',
  id: '12345',
  price: 28.99,
}
  • ์œ„์™€ ๊ฐ™์ด ํƒ€์ž…์„ ๋ช…์‹œํ•˜๋ฉด ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ๊ฐ€ ๋™์ž‘ํ•œ๋‹ค.
    • ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ๋Š” ํŠนํžˆ ์„ ํƒ์  ์†์„ฑ์ด ์žˆ๋Š” ํƒ€์ž…์˜ ์˜คํƒ€ ๊ฐ™์€ ์˜ค๋ฅ˜๋ฅผ ์žก๋Š” ๋ฐ ํšจ๊ณผ์ ์ด๋‹ค.
    • ๊ทธ๋ฆฌ๊ณ  ๋ณ€์ˆ˜๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ์ˆœ๊ฐ„์ด ์•„๋‹Œ ํ• ๋‹นํ•˜๋Š” ์‹œ์ ์— ์˜ค๋ฅ˜๊ฐ€ ํ‘œ์‹œ๋˜๋„๋ก ํ•ด์ค€๋‹ค.
  • ๋งŒ์•ฝ ํƒ€์ž… ๊ตฌ๋ฌธ์„ ์ œ๊ฑฐํ•œ๋‹ค๋ฉด ์ž‰์—ฌ ์†์„ฑ ์ฒดํฌ๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š๊ณ , ๊ฐ์ฒด๋ฅผ ์„ ์–ธํ•œ ๊ณณ์ด ์•„๋‹ˆ๋ผ ๊ฐ์ฒด๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ๊ณณ์—์„œ ํƒ€์ž… ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ ํƒ€์ž… ๊ตฌ๋ฌธ์„ ์ œ๋Œ€๋กœ ๋ช…์‹œํ•œ๋‹ค๋ฉด, ์‹ค์ œ๋กœ ์‹ค์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๋ถ€๋ถ„์— ์˜ค๋ฅ˜๋ฅผ ํ‘œ์‹œํ•ด์ค€๋‹ค.
const elmo = {
  name: 'Elmo',
  id: '12345',
  price: 28.99,
}

logProduct(elmo); // ์—ฌ๊ธฐ์„œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ!

# (4) ๋ฐ˜ํ™˜ ํƒ€์ž… ๋ช…์‹œ์˜ ์žฅ์ 


# โญ๏ธ ์˜ค๋ฅ˜ ๋ฐฉ์ง€

  • ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜์—๋„ ํƒ€์ž…์„ ๋ช…์‹œํ•˜์—ฌ ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ํƒ€์ž… ์ถ”๋ก ์ด ๊ฐ€๋Šฅํ• ์ง€๋ผ๋„ ๊ตฌํ˜„์ƒ์˜ ์˜ค๋ฅ˜๊ฐ€ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ๊นŒ์ง€ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ํƒ€์ž… ๊ตฌ๋ฌธ์„ ๋ช…์‹œํ•˜๋Š” ๊ฒŒ ์ข‹๋‹ค.
const cache: { [ticker: string]: number } = {};

function getQuote(ticker: string) {
  if (ticker in cache) {
    return cache[ticker];
  }
    
  return fetch(`https://quotes.example.com/?q=${ticker}`)
    .then(response => response.json())
    .then(quote => {
      cache[ticker] = quote;
      return quote;
    });
}
  • ์œ„ ์ฝ”๋“œ์—๋Š” ์˜ค๋ฅ˜๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ getQuote๋Š” ํ•ญ์ƒ Promise๋ฅผ returnํ•˜๋ฏ€๋กœ if ๊ตฌ๋ฌธ์—๋Š” cache[ticker]๊ฐ€ ์•„๋‹ˆ๋ผ Promise.resolve(cache[ticker])๊ฐ€ ๋ฐ˜ํ™˜๋˜๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค.
    • ์‹คํ–‰ํ•ด๋ณด๋ฉด ์˜ค๋ฅ˜๋Š” getQuote ๋‚ด๋ถ€๊ฐ€ ์•„๋‹Œ getQuote๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ์—์„œ ๋ฐœ์ƒํ•œ๋‹ค.
// ํ•จ์ˆ˜ ํ˜ธ์ถœ๋ถ€
getQuote('MSFT').then(considerBuying);
// ~~~ 'number | Promise<any>' ํ˜•์‹์— 'then' ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
// 'number' ํ˜•์‹์— 'then' ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
  • ์ด ๋•Œ ์˜๋„๋œ ๋ฐ˜ํ™˜ ํƒ€์ž…(Promise<number>)์„ ๋ช…์‹œํ•œ๋‹ค๋ฉด, ์ •ํ™•ํ•œ ์œ„์น˜์— ์˜ค๋ฅ˜๊ฐ€ ํ‘œ์‹œ๋œ๋‹ค.
const cache: { [ticker: string]: number } = {};

function getQuote(ticker: string): Promise<number> {
  if (ticker in cache) {
    return cache[ticker]; // 'number' ํ˜•์‹์€ 'Promise<number>' ํ˜•์‹์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  }
}
  • ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๋ช…์‹œํ•˜๋ฉด, ๊ตฌํ˜„์ƒ์˜ ์˜ค๋ฅ˜๊ฐ€ ์‚ฌ์šฉ์ž ์ฝ”๋“œ์˜ ์˜ค๋ฅ˜๋กœ ํ‘œ์‹œ๋˜์ง€ ์•Š๋Š”๋‹ค.
    • Promise์™€ ๊ด€๋ จ๋œ ํŠน์ • ์˜ค๋ฅ˜๋ฅผ ํ”ผํ•˜๋Š” ๋ฐ๋Š” async ํ•จ์ˆ˜๊ฐ€ ํšจ๊ณผ์ ์ด๋‹ค.

# โญ๏ธ ํ•จ์ˆ˜์— ๋Œ€ํ•ด ๋”์šฑ ๋ช…ํ™•ํ•˜๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์Œ

  • ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๋ช…์‹œํ•˜๋ ค๋ฉด ๊ตฌํ˜„ํ•˜๊ธฐ ์ „์— ์ž…๋ ฅ ํƒ€์ž…๊ณผ ์ถœ๋ ฅ ํƒ€์ž…์ด ๋ฌด์—‡์ธ์ง€ ์•Œ์•„์•ผ ํ•œ๋‹ค.
    • ์ถ”ํ›„์— ์ฝ”๋“œ๊ฐ€ ์กฐ๊ธˆ ๋ณ€๊ฒฝ๋˜์–ด๋„ ๊ทธ ํ•จ์ˆ˜์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ์‰ฝ๊ฒŒ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.
    • ์ „์ฒด ํƒ€์ž… ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๋ฉด ๊ตฌํ˜„์— ๋งž์ถ”์–ด ์ฃผ๋จน๊ตฌ๊ตฌ์‹์œผ๋กœ ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ์ž‘์„ฑ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์›ํ•˜๋Š” ๋ชจ์–‘์„ ์–ป๊ฒŒ ๋œ๋‹ค.

# โญ๏ธ ๋ช…๋ช…๋œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ

interface Vector2D {
  x: number;
  y: number;
}

function add(a: Vector2D, b: Vector2D) {
  return {
    x: a.x + b.x,
    y: a.y + b.y,
  }
}
  • ์œ„์™€ ๊ฐ™์ด ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๋ช…์‹œํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ add ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์€ { x: number; y: number; }๋กœ ์ถ”๋ก ๋œ๋‹ค.
    • ์ด๋Ÿฐ ๊ฒฝ์šฐ Vector2D์™€ ํ˜ธํ™˜๋˜์ง€๋งŒ, ์ž…๋ ฅ์ด Vector2D์ธ๋ฐ ๋ฐ˜ํ•ด ์ถœ๋ ฅ์€ Vector2D๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ ๋‹นํ™ฉ์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๋ช…์‹œํ•˜๋ฉด ๋”์šฑ ์ง๊ด€์ ์ธ ํ‘œํ˜„์ด ๋œ๋‹ค.
    • ๊ทธ๋ฆฌ๊ณ  ๋ฐ˜ํ™˜ ๊ฐ’์„ ๋ณ„๋„์˜ ํƒ€์ž…์œผ๋กœ ์ •์˜ํ•˜๋ฉด ํƒ€์ž…์— ๋Œ€ํ•œ ์ฃผ์„์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด์„œ, ๋”์šฑ ์ž์„ธํ•œ ์„ค๋ช…์ด ๊ฐ€๋Šฅํ•˜๋‹ค.(feat. TSDoc)
    • ์ถ”๋ก ๋œ ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก ๋ช…๋ช…๋œ ํƒ€์ž…์„ ์ œ๊ณตํ•˜๋Š” ์ด์ ์€ ์ปค์ง„๋‹ค.
  • ๋ฆฐํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด eslint ๊ทœ์น™ ์ค‘ no-inferrable-types์„ ์‚ฌ์šฉํ•ด์„œ ์ž‘์„ฑ๋œ ๋ชจ๋“  ํƒ€์ž… ๊ตฌ๋ฌธ์ด ์ •๋ง๋กœ ํ•„์š”ํ•œ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


# Item20. ๋‹ค๋ฅธ ํƒ€์ž…์—๋Š” ๋‹ค๋ฅธ ๋ณ€์ˆ˜ ์‚ฌ์šฉํ•˜๊ธฐ

  • ๋‹ค๋ฅธ ํƒ€์ž…์—๋Š” ๋ณ„๋„์˜ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๋ฐ”๋žŒ์งํ•œ ์ด์œ 
    • ์„œ๋กœ ๊ด€๋ จ์ด ์—†๋Š” ๋‘ ๊ฐœ์˜ ๊ฐ’์„ ๋ถ„๋ฆฌํ•œ๋‹ค.
    • ๋ณ€์ˆ˜๋ช…์„ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ์ง€์„ ์ˆ˜ ์žˆ๋‹ค.
    • ํƒ€์ž… ์ถ”๋ก ์„ ํ–ฅ์ƒ์‹œํ‚ค๋ฉฐ, ํƒ€์ž… ๊ตฌ๋ฌธ์ด ๋ถˆํ•„์š”ํ•ด์ง„๋‹ค.
    • ํƒ€์ž…์ด ์ข€ ๋” ๊ฐ„๊ฒฐํ•ด์ง„๋‹ค. (ex. string | number ๋Œ€์‹  string๊ณผ number๋ฅผ ์‚ฌ์šฉ)
    • let ๋Œ€์‹  const๋กœ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๊ฒŒ ๋œ๋‹ค. const๋กœ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•ด์ง€๊ณ , ํƒ€์ž… ์ฒด์ปค๊ฐ€ ํƒ€์ž…์„ ์ถ”๋ก ํ•˜๊ธฐ์—๋„ ์ข‹๋‹ค.
  • ๋ณ€์ˆ˜์˜ ๊ฐ’์€ ๋ฐ”๋€” ์ˆ˜ ์žˆ์ง€๋งŒ, ํƒ€์ž…์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.
  • ํ˜ผ๋ž€์„ ๋ง‰๊ธฐ ์œ„ํ•ด ํƒ€์ž…์ด ๋‹ค๋ฅธ ๊ฐ’์„ ๋‹ค๋ฃฐ ๋•Œ์—๋Š” ๋ณ€์ˆ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ํ•œ๋‹ค.

# Item21. ํƒ€์ž… ๋„“ํžˆ๊ธฐ

# (1) ํƒ€์ž… ๋„“ํžˆ๊ธฐ ๊ณผ์ •

  • ํƒ€์ž… ๋„“ํžˆ๊ธฐ ๊ณผ์ •์€ ์ง€์ •๋œ ๋‹จ์ผ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ๊ฐ’๋“ค์˜ ์ง‘ํ•ฉ์„ ์œ ์ถ”ํ•˜๋Š” ๊ณผ์ •์ด๋‹ค.
interface Vector3 {
  x: number;
  y: number;
  z: number;
}

function getComponent(vector: Vector3, axis: 'x' | 'y' | 'z') {
  return vector[axis];
}

let x = 'x';
let vec = {x: 10, y: 20, z: 30};
getComponent(vec, x); // ~ 'string' ํ˜•์‹์˜ ์ธ์ˆ˜๋Š” '"x" | "y" | "z"' ํ˜•์‹์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ์œ„ ์ฝ”๋“œ์—์„œ ๋ณด์‹œ๋‹ค์‹œํ”ผ getComponent ํ•จ์ˆ˜๋Š” ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์— "x" | "y" | "z" ํƒ€์ž…์„ ๊ธฐ๋Œ€ํ–ˆ์ง€๋งŒ, x์˜ ํƒ€์ž…์€ ํ• ๋‹น ์‹œ์ ์— ๋„“ํžˆ๊ธฐ๊ฐ€ ๋™์ž‘ํ•ด์„œ string์œผ๋กœ ์ถ”๋ก ๋˜์—ˆ๋‹ค.
    • string ํƒ€์ž…์€ "x" | "y" | "z" ํƒ€์ž…์— ํ• ๋‹น์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์˜ค๋ฅ˜๊ฐ€ ๋œ ๊ฒƒ์ด๋‹ค.
  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์•„๋ฌด๋ฆฌ ์˜๋ฆฌํ•˜๋”๋ผ๋„ ์‚ฌ๋žŒ์˜ ๋งˆ์Œ๊นŒ์ง€ ์ฝ์„ ์ˆ˜๋Š” ์—†๊ณ  ์ถ”์ธกํ•œ ๋‹ต์ด ํ•ญ์ƒ ์˜ณ์„ ์ˆ˜๋„ ์—†๋‹ค.

# (2) ํƒ€์ž… ๋„“ํžˆ๊ธฐ ๊ณผ์ •์„ ์ œ์–ดํ•˜๊ธฐ


# โญ๏ธ const ๋กœ ๋ณ€์ˆ˜ ์„ ์–ธํ•˜๊ธฐ

  • let ๋Œ€์‹  const๋กœ ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•˜๋ฉด ๋” ์ข์€ ํƒ€์ž…์ด ๋œ๋‹ค.
  • ์‹ค์ œ๋กœ ์œ„ ์ฝ”๋“œ์—์„œ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๋ฅผ const ๋กœ ์ž‘์„ฑํ•˜๋ฉด ํ•ด๊ฒฐ๋œ๋‹ค.
const x = 'x'; // ํƒ€์ž…์ด "x"
let vec = {x: 10, y: 20, z: 30};
getComponent(vec, x); // ์ •์ƒ
  • ์ด์ œ x ๊ฐ’์€ ์žฌํ• ๋‹น๋  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์˜์‹ฌ์˜ ์—ฌ์ง€ ์—†์ด ๋” ์ข์€ ํƒ€์ž…("x")์œผ๋กœ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๊ทธ๋ฆฌ๊ณ  ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž… "x"์€ "x" | "y" | "z" ์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์ฝ”๋“œ๊ฐ€ ํƒ€์ž… ์ฒด์ปค๋ฅผ ํ†ต๊ณผํ•œ๋‹ค.
  • ๊ทธ๋Ÿฌ๋‚˜ const๊ฐ€ ๋งŒ๋Šฅ์€ ์•„๋‹ˆ๋‹ค! ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์˜ ๊ฒฝ์šฐ์—๋Š” ์—ฌ์ „ํžˆ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.
    • ํŠœํ”Œ ํƒ€์ž…์„ ์ถ”๋ก ํ•ด์•ผ ํ• ์ง€, ์š”์†Œ๋“ค์€ ์–ด๋–ค ํƒ€์ž…์œผ๋กœ ์ถ”๋ก ํ•ด์•ผ ํ• ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.
const v = {
  x: 1,
};

v.x = 3;
v.x = '3';
v.y = 4;
v.name = 'JS';
  • ์œ„ ์ฝ”๋“œ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.
  • v ๊ฐ’์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์ •์˜ํ•˜๋ฉด {readonly x: 1}์ด๋‹ค. ์กฐ๊ธˆ ์ถ”์ƒ์ ์œผ๋กœ๋Š” {x: number}๊ฐ€ ๋˜๊ณ  ๊ฐ€์žฅ ์ถ”์ƒ์ ์ด๋ผ๋ฉด {[key: string]: number} ๋˜๋Š” object๊ฐ€ ๋  ๊ฒƒ์ด๋‹ค.
  • ๊ฐ์ฒด์˜ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๋„“ํžˆ๊ธฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๊ฐ ์š”์†Œ๋ฅผ let์œผ๋กœ ํ• ๋‹นํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋‹ค๋ฃฌ๋‹ค.
    • ๊ทธ๋ž˜์„œ v์˜ ํƒ€์ž…์€ {x: number}๊ฐ€ ๋œ๋‹ค.
    • ์œ„ ์ฝ”๋“œ๋ฅผ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ๊ตฌ๋™ํ•˜๋ฉด v.x = 3;์„ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ๊ตฌ๋ฌธ์€ ๋ชจ๋‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋ช…ํ™•์„ฑ๊ณผ ์œ ์—ฐ์„ฑ ์‚ฌ์ด์˜ ๊ท ํ˜•์„ ์œ ์ง€ํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
    • ์˜ค๋ฅ˜๋ฅผ ์žก๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ถฉ๋ถ„ํžˆ ๊ตฌ์ฒด์ ์œผ๋กœ ํƒ€์ž…์„ ์ถ”๋ก ํ•ด์•ผ ํ•˜์ง€๋งŒ, ์ž˜๋ชป๋œ ์ถ”๋ก ์„ ํ•  ์ •๋„๋กœ ๊ตฌ์ฒด์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.

# (3) ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ์žฌ์ •์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•


# โญ๏ธ ๋ช…์‹œ์  ํƒ€์ž… ๊ตฌ๋ฌธ์„ ์ œ๊ณต

const v: { x: 1 | 3 | 5 } = {
  x: 1,
}; // ํƒ€์ž…์ด { x: 1 | 3 | 5; }

# โญ๏ธ ํƒ€์ž… ์ฒด์ปค์— ์ถ”๊ฐ€์ ์ธ ๋ฌธ๋งฅ ์ œ๊ณต

  • ์˜ˆ๋ฅผ ๋“ค์–ด, ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๊ฐ’์„ ์ „๋‹ฌํ•˜๋Š” ๊ณผ์ •

# โญ๏ธ const ๋‹จ์–ธ๋ฌธ ์‚ฌ์šฉ

const v1 = {
  x: 1,
  y: 2,
}; // ํƒ€์ž…์€ { x: number; y: number; }

const v2 = {
  x: 1 as const,
  y: 2,
}; // ํƒ€์ž…์€ { x:1; y: number; }

const v3 = {
  x: 1,
  y: 2,
} as const; // ํƒ€์ž…์€ { readonly x: 1; readonly y: 2; }
  • ๊ฐ’ ๋’ค์— as const๋ฅผ ์ž‘์„ฑํ•˜๋ฉด, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์ตœ๋Œ€ํ•œ ์ข์€ ํƒ€์ž…์œผ๋กœ ์ถ”๋ก ํ•œ๋‹ค.
  • ์ด ๊ธฐ๋ฒ•์€ ๊ฐ์ฒด ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฐฐ์—ด์—์„œ๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋„“ํžˆ๊ธฐ๋กœ ์ธํ•ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๊ณ  ์ƒ๊ฐ๋˜๋ฉด, ๋ช…์‹œ์  ํƒ€์ž… ๊ตฌ๋ฌธ ๋˜๋Š” const ๋‹จ์–ธ๋ฌธ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

# Item22. ํƒ€์ž… ์ขํžˆ๊ธฐ

# (1) ํƒ€์ž… ์ขํžˆ๋Š” ๋ฐฉ๋ฒ•

  • ํƒ€์ž… ๋„“ํžˆ๊ธฐ์™€ ๋ฐ˜๋Œ€๋กœ ํƒ€์ž… ์ขํžˆ๊ธฐ๋Š” ๋„“์€ ํƒ€์ž…์œผ๋กœ๋ถ€ํ„ฐ ์ข์€ ํƒ€์ž…์œผ๋กœ ์ง„ํ–‰ํ•˜๋Š” ๊ณผ์ •์„ ๋งํ•œ๋‹ค.
  • ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์˜ˆ์‹œ๋Š” null ์ฒดํฌ์ด๋‹ค.
const el = document.getElementById('foo'); // ํƒ€์ž…์ด HTMLElement | null;

if (el) {
  el // ํƒ€์ž…์ด HTMLElement
  el.innerHTML = 'Party Time'.blink();
} else {
  el // ํƒ€์ž…์ด null
  alert('No element #foo!');
}
  • ์œ„์™€ ๊ฐ™์€ ์กฐ๊ฑด ๋ถ„๊ธฐ๋ฌธ์„ ํ†ตํ•ด์„œ ํƒ€์ž… ์ขํžˆ๊ธฐ๋ฅผ ์ž˜ ํ•ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
    • ํ•˜์ง€๋งŒ ํƒ€์ž… ๋ณ„์นญ์ด ์กด์žฌํ•œ๋‹ค๋ฉด ๊ทธ๋Ÿฌ์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ๋‹ค. (์ถ”ํ›„ Item24 ์—์„œ ์ž์„ธํžˆ ์ง„ํ–‰)
  • ์ด์™ธ์—๋„ ํƒ€์ž… ์ขํžˆ๋Š” ๋ฐฉ๋ฒ•์ด ๋‹ค์–‘ํ•˜๊ฒŒ ์กด์žฌํ•œ๋‹ค.
// ํƒ€์ž… ์ขํžˆ๋Š” ๋ฐฉ๋ฒ•(1) - ๋ถ„๊ธฐ๋ฌธ์—์„œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ฑฐ๋‚˜ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜

const el = document.getElementById('foo'); // ํƒ€์ž…์ด HTMLElement | null;

if (!el) throw new Error('Unable to find #foo')!
el; // ์ด์ œ ํƒ€์ž…์€ HTMLElement
el.innerHTML = 'Party Time'.blink();
// ํƒ€์ž… ์ขํžˆ๋Š” ๋ฐฉ๋ฒ•(2) - instanceof ์‚ฌ์šฉ

function contains(text: string, search: string | RegExp) {
  if (search instanceof RegExp) {
    search // ํƒ€์ž…์ด RegExp
    return !!search.exec(text);
  }
  search // ํƒ€์ž…์ด string
  return text.includes(search);
}
// ํƒ€์ž… ์ขํžˆ๋Š” ๋ฐฉ๋ฒ•(3) - ์†์„ฑ ์ฒดํฌ

interface A {
  a: number;
}

interface B {
  b: number;
}

function pickAB(ab: A | B) {
  if ('a' in ab) {
    ab // ํƒ€์ž…์ด A
  } else {
    ab // ํƒ€์ž…์ด B
  }
  ab // ํƒ€์ž…์ด A | B
}
// ํƒ€์ž… ์ขํžˆ๋Š” ๋ฐฉ๋ฒ•(4) - Array.isArray ๊ฐ™์€ ์ผ๋ถ€ ๋‚ด์žฅ ํ•จ์ˆ˜๋กœ๋„ ํƒ€์ž…์„ ์ขํž ์ˆ˜ ์žˆ์Œ

function contains(text: string, terms: string | string[]) {
  const termList = Array.isArray(terms) ? terms : [terms];
  termList // ํƒ€์ž…์ด string[]
  // ...
}

# โš ๏ธ ์„ฃ๋ถˆ๋ฆฌ ํƒ€์ž…์„ ์ขํ˜”๋‹ค๊ฐ€ ์˜ค๋ฅ˜๋ฅผ ๋ฒ”ํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ํ”ผํ•˜์ž!

  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ typeof null์€ "object" ์ด๋ฏ€๋กœ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋กœ๋Š” null ์ฒดํฌ๋ฅผ ํ•  ์ˆ˜ ์—†๋‹ค.
const el = document.getElementById('foo'); // ํƒ€์ž…์ด HTMLElement | null

if (typeof el === 'object') {
  el; // ํƒ€์ž…์ด HTMLElement | null
}
  • ๋นˆ ๋ฌธ์ž์—ด ''๊ณผ 0 ๋ชจ๋‘ false๊ฐ€ ๋˜๋ฏ€๋กœ, ํƒ€์ž…์€ ์ „ํ˜€ ์ขํ˜€์ง€์ง€ ์•Š์•˜๊ณ  x๋Š” ์—ฌ์ „ํžˆ ๋ธ”๋ก ๋‚ด์—์„œ string ๋˜๋Š” number๊ฐ€ ๋œ๋‹ค.
function foo(x?: number | string | null) {
  if (!x) {
    x; // ํƒ€์ž…์ด string | number | null | undefined;
  }
}


# (2) ํƒœ๊ทธ๋œ ์œ ๋‹ˆ์˜จ(tagged union) (๊ตฌ๋ณ„๋œ ์œ ๋‹ˆ์˜จ(discriminated union))

interface UploadEvent {
  type: 'upload';
  filename: string;
  contents: string;
}

interface DownloadEvent {
  type: 'download';
  filename: string;
}

type AppEvent = UploadEvent | DownloadEvent;

function handleEvent(e: AppEvent) {
  switch (e.type) {
    case 'download':
      e // ํƒ€์ž…์ด DownloadEvent
      break;
    case 'upload':
      e // ํƒ€์ž…์ด UploadEvent
      break;
  }
}

# (3) ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œ

function isInputElement(el: HTMLElement): el is HTMLInputElement {
  return 'value' in el;
}

function getElementContent(el: HTMLElement) {
  if (isInputElement(el)) {
    el; // ํƒ€์ž…์ด HTMLInputElement
    return el.value;
  }
  el; // ํƒ€์ž…์ด HTMLElement
  return el.textContent;
}

# (4) ํƒ€์ž… ๊ฐ€๋“œ๋ฅผ ์ด์šฉํ•œ ๋ฐฐ์—ด๊ณผ ๊ฐ์ฒด์—์„œ์˜ ํƒ€์ž… ์ขํžˆ๊ธฐ

  • ๋ฐฐ์—ด์—์„œ ์–ด๋–ค ํƒ์ƒ‰์„ ์ˆ˜ํ–‰ํ•  ๋•Œ undefined๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
const jackson5 = ['Jackie', 'Tito', 'Jermaine', 'Marlon', 'Michael'];

const members = ['Janet', 'Michael'].map(
  who => jackson5.find(n => n === who)
); // ํƒ€์ž…์ด (string | undefined)[]
  • filter ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด undefined๋ฅผ ๊ฑธ๋Ÿฌ ๋‚ด๋ ค๊ณ  ํ•ด๋„ ์ž˜ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
const members = ['Janet', 'Michael'].map(
  who => jackson5.find(n => n === who)
).filter(who => who !== undefined); // ํƒ€์ž…์ด (string | undefined)[]
  • ์ด๋Ÿด ๋•Œ ํƒ€์ž… ๊ฐ€๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํƒ€์ž…์„ ์ขํž ์ˆ˜ ์žˆ๋‹ค.
function isDefined<T>(x: T | undefined): x is T {
  return x !== undefined;
}

const members = ['Janet', 'Michael'].map(
  who => jackson5.find(n => n === who)
).filter(isDefined); // ํƒ€์ž…์ด string[]

# Item23. ํ•œ๊บผ๋ฒˆ์— ๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ธฐ

  • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋Š” ์†์„ฑ์„ ํ•˜๋‚˜์”ฉ ์ถ”๊ฐ€ํ•˜๊ธฐ๋ณด๋‹ค๋Š” ์—ฌ๋Ÿฌ ์†์„ฑ์„ ํฌํ•จํ•ด์„œ ํ•œ๊บผ๋ฒˆ์— ์ƒ์„ฑํ•ด์•ผ ํƒ€์ž… ์ถ”๋ก ์— ์œ ๋ฆฌํ•˜๋‹ค.
const pt = {};
pt.x = 3; // ~ '{}' ํ˜•์‹์— 'x' ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
pt.y = 4; // ~ '{}' ํ˜•์‹์— 'y' ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
interface Point {
  x: number;
  y: number;
}

// ๋ฌผ๋ก  ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฝ์šฐ ํƒ€์ž… ๋‹จ์–ธ๋ฌธ์„ ์ด์šฉํ•ด const pt = {} as Point;๋กœ ์ž‘์„ฑํ•ด์„œ ํ•ด๊ฒฐ์€ ํ•  ์ˆ˜ ์žˆ๋‹ค.
const pt: Point = {}; // ~~ '{}' ํ˜•์‹์— 'Point' ํ˜•์‹์˜ x, y ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
pt.x = 3;
pt.y = 4;
const pt = {
  x: 3,
  y: 4,
}; // ์ •์ƒ
  • ์ž‘์€ ๊ฐ์ฒด๋“ค์„ ์กฐํ•ฉํ•ด์„œ ํฐ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ๊ฐ์ฒด ์ „๊ฐœ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํฐ ๊ฐ์ฒด๋ฅผ ํ•œ๊บผ๋ฒˆ์— ๋งŒ๋“ค์–ด๋‚ด๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    • ๊ฐ์ฒด ์ „๊ฐœ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํƒ€์ž… ๊ฑฑ์ • ์—†์ด ํ•„๋“œ ๋‹จ์œ„๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์ด ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์šฐํšŒํ•˜๊ธฐ๋Š” ํ–ˆ์ง€๋งŒ, ๊ฐ์ฒด์— ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์œ ์šฉํ•˜๋‹ค.

  • ํƒ€์ž…์— ์•ˆ์ „ํ•œ ๋ฐฉ์‹์œผ๋กœ ์กฐ๊ฑด๋ถ€ ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด, ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š” null ๋˜๋Š” {}์œผ๋กœ ๊ฐ์ฒด ์ „๊ฐœ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
declare let hasMiddle: boolean;

const firstLast = {first: 'Harry', last: 'Truman'};
const president = {...firstLast, ...(hasMiddle ? {middle: 'S'} : {})};
  • ์œ„ ์ฝ”๋“œ๋ฅผ ํŽธ์ง‘๊ธฐ์—์„œ president์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํƒ€์ž…์ด ์ถ”๋ก ๋œ๋‹ค.
const persident: {
  middle?: string;
  first: string;
  last: string;
}
  • ์ „๊ฐœ ์—ฐ์‚ฐ์ž๋กœ ํ•œ๊บผ๋ฒˆ์— ์—ฌ๋Ÿฌ ์†์„ฑ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
declare let hasDates: boolean;

const nameTitle = {name: 'Khufu', title: 'Pharaoh'};
const pharaoh = {
  ...nameTitle,
  ...(hasDates ? {start: -2589, end: -2566} : {})
};

pharaoh.start // ํƒ€์ž… ์˜ค๋ฅ˜
  • ์œ„ ์ฝ”๋“œ๋ฅผ ํŽธ์ง‘๊ธฐ์—์„œ pharaoh์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํƒ€์ž…์ด ์ถ”๋ก ๋œ๋‹ค.
const pharaoh: {
  start: number;
  end: number;
  name: string;
  title: string;
} | {
  name: string;
  title: string;
}
  • ๋ฌด์–ธ๊ฐ€ ์›์น˜ ์•Š์€ ํƒ€์ž… ํ˜•ํƒœ๋กœ ์ถ”๋ก ์ด ๋˜์—ˆ๋‹ค.
    • ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ์œ ๋‹ˆ์˜จ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๊ฐ€๋Šฅํ•œ ๊ฐ’์˜ ์ง‘ํ•ฉ์„ ๋” ์ •ํ™•ํžˆ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ํ•˜์ง€๋งŒ ์œ ๋‹ˆ์˜จ ๋ณด๋‹ค๋Š” ์„ ํƒ์  ํ•„๋“œ๊ฐ€ ๋‹ค๋ฃจ๊ธฐ์—๋Š” ๋” ์‰ฌ์šธ ์ˆ˜ ์žˆ๋‹ค.
    • ์„ ํƒ์  ํ•„๋“œ ๋ฐฉ์‹์œผ๋กœ ํ‘œํ˜„ํ•˜๋ ค๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ํ—ฌํผ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
function addOptional<T extends object, U extends object>(
  a: T, b: U | null
): T & Partial<U> {
  return {...a, ...b};  
}

const pharaoh = addOptional(
  nameTitle,
  (hasDates ? {start: -2589, end: -2566} : null
);
    
pharaoh.start // ์ •์ƒ, ํƒ€์ž…์ด number | undefined