TypeScript Course ๐Ÿ”ฅ

์›๋ฏผ๊ด€ยท2025๋…„ 5์›” 14์ผ

[TIL]

๋ชฉ๋ก ๋ณด๊ธฐ
180/201
post-thumbnail

0. Overview ๐ŸŒ€

ReactJS์™€ NestJS๋ฅผ ๋ฉ”์ธ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋กœ์„œ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ์ดํ•ด๋Š” ํ•„์ˆ˜์ ์ด๊ฒ ์ฃ . freeCodeCamp์˜ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ๊ฐ•์˜ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ, TS์˜ ๊ธฐ์ดˆ์— ๋Œ€ํ•ด ์ •๋ฆฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์˜ค๋Š˜์€ Bob Ziroll(๋ฐฅ ์ง€๋กค) ์„ ์ƒ๋‹˜์„ ๋ชจ์…จ์Šต๋‹ˆ๋‹ค. ๋ฐœ์Œ์ฃผ์˜

reference: https://www.youtube.com/watch?v=SpwzRDUQ1GI&t=330s

1. Introduction ๐ŸŒ€

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ(์ดํ•˜ TS)๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ(์ดํ•˜ JS)์˜ Superset์ž…๋‹ˆ๋‹ค. ์ฆ‰, JS์— ํƒ€์ž… ์ง€์ • ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๊ฐ€ TS๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ชจ๋“  ์œ ํšจํ•œ JS ์ฝ”๋“œ๋Š” TS์—์„œ๋„ ์œ ํšจํ•ฉ๋‹ˆ๋‹ค.

Bob Ziroll ์„ ์ƒ๋‹˜๊ป˜์„œ๋Š”, TS๊ฐ€ ๊ฐœ๋ฐœ์ž์˜ ์ž์‹ ๊ฐ๊ณผ ์ƒ์‚ฐ์„ฑ์„ ์ œ๊ณ ํ•จ๊ณผ ๋™์‹œ์— ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ œ๋ฅผ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ์–ด์„œ, TS๋ฅผ ๋ฐฐ์›Œ์•ผ ํ•œ๋‹ค๊ณ  ๋ง์”€ํ•˜์‹ญ๋‹ˆ๋‹ค. ์ €๋„ ์ „์ ์œผ๋กœ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.


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

Bob Ziroll ์„ ์ƒ๋‹˜์˜ ์นœ๊ตฌ๋ถ„๊ป˜์„œ ์ด๋Ÿฐ ๋ง์”€์„ ํ•˜์…จ๋‹ต๋‹ˆ๋‹ค.

TS is not making your life terrible. It's just showing you how terrible your life already is.

์ธ์ •ํ•˜๊ธฐ ์‹ซ์Šต๋‹ˆ๋‹ค.(์ •ํ™•ํ•œ ํŒฉํŠธ๋ผ๋Š” ๋œป)

๊ฐ„๋‹จํ•œ Pizza App์„ ๋งŒ๋“ค๋ฉฐ, TS ๊ด€๋ จ ์ด๋ก ์„ ์ •๋ฆฌํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

2. Pizza App Fundamentals ๐ŸŒ€

2-1. Intro to Pizza App ๐ŸŸข

// ์ดˆ๊ธฐ ํ”ผ์ž ๋ฉ”๋‰ด ๋ฐฐ์—ด. ๊ฐ ํ”ผ์ž๋Š” ์ด๋ฆ„(name)๊ณผ ๊ฐ€๊ฒฉ(price)์„ ๊ฐ€์ง.
const menu = [
  { name: "Margherita", price: 8 },
  { name: "Pepperoni", price: 10 },
  { name: "Hawaiian", price: 10 },
  { name: "Veggie", price: 9 },
];
// ํ”ผ์ž ํŒ๋งค ์ˆ˜์ต์„ ์ €์žฅํ•˜๋Š” ๋ณ€์ˆ˜.
// const๋กœ ์„ ์–ธ๋˜์–ด ๊ฐ’ ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์•„๋ž˜์—์„œ ๋ณ€๊ฒฝ์„ ์‹œ๋„ํ•จ (์˜๋„๋œ ์˜ค๋ฅ˜).
const cashInRegister = 100;

// ์ฃผ๋ฌธ์— ๋ถ€์—ฌํ•  ๊ณ ์œ  ID๋ฅผ ์œ„ํ•œ ๋ณ€์ˆ˜.
// const๋กœ ์„ ์–ธ๋˜์–ด ์•„๋ž˜์—์„œ ++ ์—ฐ์‚ฐ ์‹œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ (์˜๋„๋œ ์˜ค๋ฅ˜).
const nextOrderId = 1;

// ํ˜„์žฌ ์ฒ˜๋ฆฌ ์ค‘์ธ ์ฃผ๋ฌธ ๋ชฉ๋ก์„ ์ €์žฅํ•  ํ ๋ฐฐ์—ด.
const orderQueue = [];
// ์ƒˆ๋กœ์šด ํ”ผ์ž ๋ฉ”๋‰ด ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜.
// pizzaObj๋Š” { name: string, price: number } ํ˜•์‹์ด์–ด์•ผ ํ•จ.
// ์ „๋‹ฌ๋œ ํ”ผ์ž ๊ฐ์ฒด๋ฅผ ๋ฉ”๋‰ด์— ์ถ”๊ฐ€
function addNewPizza(pizzaObj) {
  menu.push(pizzaObj);
}
// ํ”ผ์ž ์ฃผ๋ฌธ์„ ์ƒ์„ฑํ•˜๊ณ  ์ฃผ๋ฌธ ํ์— ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜.
// ์ฃผ๋ฌธ ์‹œ ์„ ํƒํ•œ ํ”ผ์ž์˜ ๊ฐ€๊ฒฉ์„ cashInRegister์— ์ถ”๊ฐ€ํ•˜๊ณ , ์ฃผ๋ฌธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•จ.
// ์‚ฌ์šฉ์ž๊ฐ€ ์ฃผ๋ฌธํ•œ ํ”ผ์ž์˜ ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•˜๋Š” ํ•ญ๋ชฉ์„ ๋ฉ”๋‰ด์—์„œ ์ฐพ์Œ
// ์„ ํƒ๋œ ํ”ผ์ž์˜ ๊ฐ€๊ฒฉ์„ ์ˆ˜์ต์— ๋”ํ•จ.
// ํ•˜์ง€๋งŒ cashInRegister๋Š” const๋ผ์„œ ์ด ์ค„์—์„œ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ ๋ฐœ์ƒ ์˜ˆ์ •.
// ์ƒˆ๋กœ์šด ์ฃผ๋ฌธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ. ๊ณ ์œ  ID, ํ”ผ์ž ์ •๋ณด, ์ƒํƒœ ํฌํ•จ.
// nextOrderId๋Š” const๋กœ ์„ ์–ธ๋˜์–ด ์žˆ์–ด์„œ ++ ์—ฐ์‚ฐ ์‹œ ์—๋Ÿฌ ๋ฐœ์ƒ ์˜ˆ์ •.
// ์ƒ์„ฑ๋œ ์ฃผ๋ฌธ์„ ์ฃผ๋ฌธ ํ์— ์ถ”๊ฐ€
// ์ƒ์„ฑ๋œ ์ฃผ๋ฌธ์„ ๋ฐ˜ํ™˜
function placeOrder(pizzaName) {
  const selectedPizza = menu.find((pizzaObj) => pizzaObj.name === pizzaName);

  cashInRegister += selectedPizza.price;

  const newOrder = {
    id: nextOrderId++,
    pizza: selectedPizza,
    status: "ordered",
  };

  orderQueue.push(newOrder);

  return newOrder;
}
// ํŠน์ • ์ฃผ๋ฌธ์„ ์™„๋ฃŒ ์ƒํƒœ๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ํ•จ์ˆ˜
// ์ „๋‹ฌ๋œ ID์™€ ์ผ์น˜ํ•˜๋Š” ์ฃผ๋ฌธ์„ ์ฃผ๋ฌธ ํ์—์„œ ์ฐพ์Œ
// ์ฐพ์€ ์ฃผ๋ฌธ์˜ ์ƒํƒœ๋ฅผ "complete"์œผ๋กœ ๋ณ€๊ฒฝ
// ์—…๋ฐ์ดํŠธ๋œ ์ฃผ๋ฌธ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
function completeOrder(orderId) {
  const order = orderQueue.find((order) => order.id === orderId);

  order.status = "complete";

  return order;
}
// ์ƒˆ๋กœ์šด ํ”ผ์ž ํ•ญ๋ชฉ๋“ค์„ ๋ฉ”๋‰ด์— ์ถ”๊ฐ€ํ•จ
// ํ•˜์ง€๋งŒ price๊ฐ€ ์•„๋‹Œ cost๋ผ๋Š” ์ž˜๋ชป๋œ ํ‚ค๋ฅผ ์‚ฌ์šฉ โ†’ ์ดํ›„ ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ ์‹œ ๋ฌธ์ œ ๋ฐœ์ƒ
addNewPizza({ name: "Chicken Bacon Ranch", cost: 12 });
addNewPizza({ name: "BBQ Chicken", cost: 12 });
addNewPizza({ name: "Spicy Sausage", cost: 11 });

// "Chicken Bacon Ranch" ํ”ผ์ž๋ฅผ ์ฃผ๋ฌธํ•จ
// ์œ„์—์„œ price๊ฐ€ ์•„๋‹ˆ๋ผ cost๋กœ ์ถ”๊ฐ€ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— selectedPizaa.price๋Š” undefined๊ฐ€ ๋˜๊ณ ,
// cashInRegister += undefined โ†’ NaN ๋ฐœ์ƒ
placeOrder("Chicken Bacon Ranch");

// ๋ฌธ์ž์—ด "1"์„ ID๋กœ ์ „๋‹ฌ โ†’ ์‹ค์ œ ์ฃผ๋ฌธ ID๋Š” ์ˆซ์ž 1์ด๋ฏ€๋กœ ๋น„๊ต ์‹œ false๊ฐ€ ๋˜์–ด
// order๋Š” undefined๊ฐ€ ๋˜๊ณ , ์ดํ›„ order.status ์ ‘๊ทผ ์‹œ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ ๋ฐœ์ƒ ๊ฐ€๋Šฅ
completeOrder("1");

// ์ตœ์ข… ๊ฒฐ๊ณผ ์ถœ๋ ฅ
console.log("Menu: ", menu);
console.log("Cash in register: ", cashInRegister);
console.log("Order Queue: ", orderQueue);

ํ”ผ์ž ์ฃผ๋ฌธ ๋กœ์ง์„ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐํ•จ์ด ๋งŽ์€ JS ์ฝ”๋“œ์ด๊ณ , ์ž ์žฌ์ ์ธ ๊ฒฐํ•จ์€ ์ฃผ์„์œผ๋กœ ํ‘œ๊ธฐํ•ด๋†“์€ ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค.

ํ˜„์žฌ์˜ .js ํŒŒ์ผ์—์„œ .ts ํŒŒ์ผ๋กœ ๋™์ผํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด, ์—„์ฒญ๋‚œ ์—๋Ÿฌ ๋ผ์ธ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

2-2. Move code to TS ๐ŸŸข

index.ts ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ , ๊ธฐ์กด์— index.js์— ์ž‘์„ฑํ–ˆ๋˜ ์ฝ”๋“œ๋ฅผ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•˜๊ฒŒ TypeError: Assignment to constant variable. ๋ถ€๋ถ„๋งŒ ์ˆ˜์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

// ํ”ผ์ž ํŒ๋งค ์ˆ˜์ต์„ ์ €์žฅํ•˜๋Š” ๋ณ€์ˆ˜.
// const๋กœ ์„ ์–ธ๋˜์–ด ๊ฐ’ ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์•„๋ž˜์—์„œ ๋ณ€๊ฒฝ์„ ์‹œ๋„ํ•จ (์˜๋„๋œ ์˜ค๋ฅ˜).
// let์œผ๋กœ ์ˆ˜์ •
let cashInRegister = 100;

// ์ฃผ๋ฌธ์— ๋ถ€์—ฌํ•  ๊ณ ์œ  ID๋ฅผ ์œ„ํ•œ ๋ณ€์ˆ˜.
// const๋กœ ์„ ์–ธ๋˜์–ด ์•„๋ž˜์—์„œ ++ ์—ฐ์‚ฐ ์‹œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ (์˜๋„๋œ ์˜ค๋ฅ˜).
// let์œผ๋กœ ์ˆ˜์ •
let nextOrderId = 1;

// ํ˜„์žฌ ์ฒ˜๋ฆฌ ์ค‘์ธ ์ฃผ๋ฌธ ๋ชฉ๋ก์„ ์ €์žฅํ•  ํ ๋ฐฐ์—ด.
const orderQueue = [];

const๋ฅผ let์œผ๋กœ ๋ณ€๊ฒฝํ•œ ๊ฒƒ์ด ์ค‘์š”ํ•œ ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค.

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

2-3. Defensive coding ๐ŸŸข

์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉฐ, ๋‹ค์–‘ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€์— ๋Œ€ํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์—๋Š” ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

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

๋ฐ˜๋ฉด Sad Path๋Š”, ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ์„ ์ธ์ง€ํ•˜๊ณ , ์˜ˆ์™ธ ์ƒํ™ฉ์ด๋‚˜ ์—์ง€ ์ผ€์ด์Šค๋ฅผ ์˜ˆ์ƒํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ œ ์ƒํ™ฉ์„ ๊ณ ๋ คํ•˜๊ณ , ๊ฐ ๋ฌธ์ œ์— ๋Œ€ํ•œ ๋ฐฉ์–ด์ ์ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜์ฃ .

TS๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ Sad Path๋ฅผ ๊ณ ๋ คํ•˜๋„๋ก ๊ฐ•์ œํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „์— ์ž‘์„ฑํ•œ JS ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

function placeOrder(pizzaName) {
  const selectedPizza = menu.find((pizzaObj) => pizzaObj.name === pizzaName);

  cashInRegister += selectedPizza.price;

  const newOrder = {
    id: nextOrderId++,
    pizza: selectedPizza,
    status: "ordered",
  };

  orderQueue.push(newOrder);

  return newOrder;
}

selectedPizza๋Š”, ๋ฉ”๋‰ด์—์„œ ๊ณ ๊ฐ์ด ์ฃผ๋ฌธํ•œ ํŠน์ • ํ”ผ์ž์ž…๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๊ณ ๊ฐ์ด ๋ฉ”๋‰ด์— ์—†๋Š” ํ”ผ์ž๋ฅผ ์ฃผ๋ฌธํ•œ๋‹ค๋ฉด selectedPizza๋Š” undefined๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ํ›„์† ์ฝ”๋“œ์—์„œ selectedPizza์˜ price์— ์ ‘๊ทผํ•˜๊ณ  ์žˆ๋Š”๋ฐ, selectedPizza๊ฐ€ undefined๋ผ๋ฉด ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

selectedPizza๊ฐ€ undefined ์ธ์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ์–ด์ ์ธ ์กฐ๊ฑด๋ฌธ์„ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

function placeOrder(pizzaName) {
  const selectedPizza = menu.find((pizzaObj) => pizzaObj.name === pizzaName);

  if (!selectedPizza) {
    console.error(`${pizzaName} does not exist in the menu.`);
    return;
  }

  cashInRegister += selectedPizza.price;

  const newOrder = {
    id: nextOrderId++,
    pizza: selectedPizza,
    status: "ordered",
  };

  orderQueue.push(newOrder);

  return newOrder;
}

๐Ÿšจ ์ค‘๊ฐ„ ์š”์•ฝ
ํ”ผ์ž ์ฃผ๋ฌธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์˜ JS์—์„œ TS๋กœ์˜ ์ „ํ™˜ ๊ณผ์ •๊ณผ ๋ฐฉ์–ด์  ์ฝ”๋”ฉ์˜ ์ค‘์š”์„ฑ์— ๊ด€ํ•ด ์„ค๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. JS์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—ฌ๋Ÿฌ ์ž ์žฌ์  ์˜ค๋ฅ˜๋“ค(์ƒ์ˆ˜ ์žฌํ• ๋‹น, undefined ์ฐธ์กฐ ๋“ฑ)์ด TS๋ฅผ ํ†ตํ•ด ์ปดํŒŒ์ผ ์‹œ์ ์— ๊ฐ์ง€๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, 'Happy Path'๊ฐ€ ์•„๋‹Œ 'Sad Path'๋ฅผ ๊ณ ๋ คํ•œ ๋ฐฉ์–ด์  ์ฝ”๋”ฉ ๋ฐฉ์‹์„ ํ†ตํ•ด ๋ฉ”๋‰ด์— ์—†๋Š” ํ”ผ์ž ์ฃผ๋ฌธ๊ณผ ๊ฐ™์€ ์˜ˆ์™ธ ์ƒํ™ฉ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ค‘์š”์„ฑ์„ ๊ฐ•์กฐํ–ˆ์Šต๋‹ˆ๋‹ค.

3. TypeScript Basics ๐ŸŒ€

3-1. Obligatory types basics lesson ๐ŸŸข

Obligatory types๋ž€, ๋ง ๊ทธ๋Œ€๋กœ TS์—์„œ ํ•„์ˆ˜์ ์œผ๋กœ ์•Œ์•„์•ผ ํ•˜๋Š” ๊ธฐ๋ณธ ํƒ€์ž… ๊ฐœ๋…์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

let myName: string = "minkwan";
let numberOfWheels: number = 4;
let isStudent: boolean = false;

๋ณ€์ˆ˜์˜ ๊ธฐ๋ณธ ํƒ€์ž…์„ ์„ค์ •ํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์œ„ ๋ฐฉ์‹์ด Pizza App์—์„œ ์–ด๋–ป๊ฒŒ ์ ์šฉ๋˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

3-2. Add type to orderId ๐ŸŸข

function completeOrder(orderId: number) {
  const order = orderQueue.find((order) => order.id === orderId);

  order.status = "complete";

  return order;
}

completeOrder ํ•จ์ˆ˜๊ฐ€ ๋ฐ›๋Š” orderId ๋ณ€์ˆ˜์˜ ๊ธฐ๋ณธ ํƒ€์ž…์„ number๋กœ ์ง€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ฒ˜์—์„œ๋Š” number๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

completeOrder(1);

๋ฌธ์ž์—ด์ด ์•„๋‹ˆ๋ผ number๋ฅผ ์ ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

3-3. Defining Custom Types ๐ŸŸข

๋ณ€์ˆ˜๊ฐ€ ๊ฐ€์ ธ์•ผ ํ•  ์†์„ฑ๊ณผ ๊ฐ ์†์„ฑ์˜ ํƒ€์ž…์„ ์ปค์Šคํ…€ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type Person = {
  name: string;
  age: number;
  isStudent: boolean;
};

let person1: Person = {
  name: "Joe",
  age: 42,
  isStudent: true,
};

let person2: Person = {
  name: "minkwan",
  age: 28,
  isStudent: false,
};

person1๊ณผ person2๋Š”, Person ์ปค์Šคํ…€ ํƒ€์ž…์— ๋ช…์‹œ๋œ ๋‚ด์šฉ์„ ๋”ฐ๋ฅด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๐Ÿšจ ์ค‘๊ฐ„ ์š”์•ฝ
TS์—์„œ ํ•„์ˆ˜์ ์œผ๋กœ ์•Œ์•„์•ผ ํ•  ๊ธฐ๋ณธ ํƒ€์ž…(string, number, boolean ๋“ฑ)์„ ๋ณ€์ˆ˜์— ์ง€์ •ํ•˜๋Š” ๋ฐฉ์‹์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํƒ€์ž…์„ ๋ช…์‹œํ•˜์—ฌ(์˜ˆ: orderId๋ฅผ number๋กœ ์ง€์ •) ํ˜ธ์ถœ ์‹œ ์ ์ ˆํ•œ ํƒ€์ž…๋งŒ ํ—ˆ์šฉํ•˜๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€์ ์œผ๋กœ, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ํ•„์š”ํ•œ ์†์„ฑ๊ณผ ๊ทธ ํƒ€์ž…์„ ์ •์˜ํ•œ ์ปค์Šคํ…€ ํƒ€์ž…(์˜ˆ: Person ํƒ€์ž…)์„ ๋งŒ๋“ค์–ด ์—ฌ๋Ÿฌ ๋ณ€์ˆ˜๊ฐ€ ๋™์ผํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ดค์Šต๋‹ˆ๋‹ค.

4. Working with Types ๐ŸŒ€

4-1. Adding a Pizza type ๐ŸŸข

์œ„์—์„œ ํ•™์Šตํ•œ ๋Œ€๋กœ Pizza๋ผ๋Š” ์ปค์Šคํ…€ ํƒ€์ž…์„ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

type Pizza = {
  name: string;
  price: number;
};

๋ฉ”๋‰ด์— ์ƒˆ๋กœ์šด ํ”ผ์ž๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ, ์œ„์—์„œ ์ •์˜ํ•œ ์ปค์Šคํ…€ ํƒ€์ž…์„ ๋”ฐ๋ฅด๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

function addNewPizza(pizzaObj: Pizza) {
  menu.push(pizzaObj);
}

์ด์ „ ์ฝ”๋“œ์—์„œ addNewPizza() ํ•จ์ˆ˜์— price๊ฐ€ ์•„๋‹Œ cost๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปค์Šคํ…€ ํƒ€์ž…์— ๋งž๊ฒŒ cost๋ฅผ price๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

addNewPizza({ name: "Chicken Bacon Ranch", cost: 12 });
addNewPizza({ name: "BBQ Chicken", cost: 12 });
addNewPizza({ name: "Spicy Sausage", cost: 11 });
addNewPizza({ name: "Chicken Bacon Ranch", price: 12 });
addNewPizza({ name: "BBQ Chicken", price: 12 });
addNewPizza({ name: "Spicy Sausage", price: 11 });

pizzaName์˜ ํƒ€์ž…๋„ string์œผ๋กœ ์ง€์ •ํ•ฉ์‹œ๋‹ค.

function placeOrder(pizzaName: string) {
  const selectedPizza = menu.find((pizzaObj) => pizzaObj.name === pizzaName);

  if (!selectedPizza) {
    console.error(`${pizzaName} does not exist in the menu.`);
    return;
  }

  cashInRegister += selectedPizza.price;

  const newOrder = {
    id: nextOrderId++,
    pizza: selectedPizza,
    status: "ordered",
  };

  orderQueue.push(newOrder);

  return newOrder;
}

...

placeOrder("Chicken Bacon Ranch");

...

4-2. Nested object types ๐ŸŸข

TS์—์„œ ์ค‘์ฒฉ ๊ฐ์ฒด ํƒ€์ž…(Nested Object Types)์„ ์ •์˜ํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์†Œ๊ฐœํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

address๋ฅผ Person ํƒ€์ž… ์•ˆ์—์„œ ์ง์ ‘ ๊ฐ์ฒด ๊ตฌ์กฐ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. address ๊ตฌ์กฐ๊ฐ€ ๋ฐ˜๋ณต๋˜๋ฉด ์ค‘๋ณต์ด ์ƒ๊ธฐ๊ณ , ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค.

type Person = {
  name: string;
  age: number;
  isStudent: boolean;
  address: {
    street: string;
    city: string;
    country: string;
  };
};

let person1: Person = {
  name: "Joe",
  age: 42,
  isStudent: true,
  address: {
    street: "123 Main",
    city: "Anytown",
    country: "USA",
  },
};

let person2: Person = {
  name: "minkwan",
  age: 28,
  isStudent: false,
  address: {
    street: "123 Main",
    city: "Anytown",
    country: "USA",
  },
};

์ด๋ฒˆ์—” address์˜ ํƒ€์ž…์„ ๋”ฐ๋กœ Address ํƒ€์ž…์œผ๋กœ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. Person ํƒ€์ž…์€ ์ด์ œ address: Address๋ผ๊ณ  ๋ช…์‹œํ•ด์„œ Address ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํƒ€์ž…์—์„œ๋„ Address ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type Address = {
  street: string;
  city: string;
  country: string;
};

type Person = {
  name: string;
  age: number;
  isStudent: boolean;
  address: Address;
};

let person1: Person = {
  name: "Joe",
  age: 42,
  isStudent: true,
  address: {
    street: "123 Main",
    city: "Anytown",
    country: "USA",
  },
};

let person2: Person = {
  name: "minkwan",
  age: 28,
  isStudent: false,
  address: {
    street: "123 Main",
    city: "Anytown",
    country: "USA",
  },
};

4-3. Optional properties ๐ŸŸข

์†์„ฑ ์ด๋ฆ„ ๋’ค์— ?๋ฅผ ๋ถ™์ด๋ฉด, ํ•ด๋‹น ์†์„ฑ์„ optional ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. person1์—์„œ address๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

type Address = {
  street: string;
  city: string;
  country: string;
};

type Person = {
  name: string;
  age: number;
  isStudent: boolean;
  address?: Address;
};

let person1: Person = {
  name: "Joe",
  age: 42,
  isStudent: true,
};

let person2: Person = {
  name: "minkwan",
  age: 28,
  isStudent: false,
  address: {
    street: "123 Main",
    city: "Anytown",
    country: "USA",
  },
};

๊ทธ๋Ÿฐ๋ฐ ์†์„ฑ์„ optional ํ•˜๊ฒŒ ์ง€์ •ํ•  ๋•Œ์—๋Š” ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

function displayInfo(person) {
  console.log(person.name);
  console.log(person.address?.street);
}

displayInfo(person1);

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด person1์—๋Š” address ์†์„ฑ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์—, ๋‹จ์ˆœํžˆ person.address.street๋กœ ์ ‘๊ทผํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. optional ํ•˜๊ฒŒ ์ง€์ •ํ•œ ๋งŒํผ, ํƒ€์ž… ์•ˆ์ •์„ฑ์ด ๊ฐ์†Œ๋œ ๊ฒƒ์ด์ง€์š”. ๊ทธ๋ž˜์„œ ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ person.address?.street๋กœ ์˜ต์…”๋„ ์ฒด์ด๋‹์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

4-4. Adding an Order type ๐ŸŸข

Order type์„ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

type Order = {
  id: number;
  pizza: Pizza;
  status: string;
};

Order type์„ orderQueue์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

const orderQueue: Order = [];

Order๋Š” ๊ฐ์ฒด ํƒ€์ž…์ธ๋ฐ orderQueue๋Š” ๋ฐฐ์—ด์ด๋ผ๋Š” ๋ฌธ์ œ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

4-5. Typing arrays ๐ŸŸข

type Person = {
  name: string;
  age: number;
  isStudent: boolean;
};

let person1: Person = {
  name: "Joe",
  age: 42,
  isStudent: true,
};

let person2: Person = {
  name: "minkwan",
  age: 28,
  isStudent: false,
};

let people: Person[] = [person1, person2];

ํ•ต์‹ฌ์€ let people: Person[] = [person1, person2];์ž…๋‹ˆ๋‹ค.

people์ด Person ๊ฐ์ฒด๋“ค๋กœ ๊ตฌ์„ฑ๋œ ๋ฐฐ์—ด ํƒ€์ž…์ž„์„ ๋ช…์‹œํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์ด ๋‚ด์šฉ์„ Pizza App์— ์ ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

4-6. Type orderQueue ๐ŸŸข

orderQueue๊ฐ€ Order๋กœ ๊ตฌ์„ฑ๋œ '๋ฐฐ์—ด ํƒ€์ž…'์ž„์„ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.

const orderQueue: Order[] = [];
function completeOrder(orderId: number) {
  const order = orderQueue.find((order) => order.id === orderId);

  order.status = "complete";

  return order;
}

order ๊ฐ’์ด ์—†์„ ์ˆ˜๋„ ์žˆ๊ฒ ์ฃ . ์ด์— ๋Œ€ํ•œ ๋ฐฉ์–ด์ ์ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

function completeOrder(orderId: number) {
  const order = orderQueue.find((order) => order.id === orderId);

  if (!order) {
    console.error(`${orderId} was not found.`);
    return;
  }

  order.status = "complete";

  return order;
}

๋ชจ๋“  ์˜ค๋ฅ˜๊ฐ€ ์ˆ˜์ •๋œ TS ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

type Pizza = {
  name: string;
  price: number;
};

type Order = {
  id: number;
  pizza: Pizza;
  status: string;
};

const menu = [
  { name: "Margherita", price: 8 },
  { name: "Pepperoni", price: 10 },
  { name: "Hawaiian", price: 10 },
  { name: "Veggie", price: 9 },
];

let cashInRegister = 100;

let nextOrderId = 1;

const orderQueue: Order[] = [];

function addNewPizza(pizzaObj: Pizza) {
  menu.push(pizzaObj);
}

function placeOrder(pizzaName: string) {
  const selectedPizza = menu.find((pizzaObj) => pizzaObj.name === pizzaName);

  if (!selectedPizza) {
    console.error(`${pizzaName} does not exist in the menu.`);
    return;
  }

  cashInRegister += selectedPizza.price;

  const newOrder = {
    id: nextOrderId++,
    pizza: selectedPizza,
    status: "ordered",
  };

  orderQueue.push(newOrder);

  return newOrder;
}

function completeOrder(orderId: number) {
  const order = orderQueue.find((order) => order.id === orderId);

  if (!order) {
    console.error(`${orderId} was not found.`);
    return;
  }

  order.status = "complete";

  return order;
}

addNewPizza({ name: "Chicken Bacon Ranch", price: 12 });
addNewPizza({ name: "BBQ Chicken", price: 12 });
addNewPizza({ name: "Spicy Sausage", price: 11 });

placeOrder("Chicken Bacon Ranch");

completeOrder(1);

console.log("Menu: ", menu);
console.log("Cash in register: ", cashInRegister);
console.log("Order Queue: ", orderQueue);

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

5. Advanced Type Features ๐ŸŒ€

5-1. Literal types ๐ŸŸข

๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์ด๋ž€, ๊ฐ’ ์ž์ฒด๋ฅผ ํƒ€์ž…์œผ๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค.

let myName: "Bob" = "Bobby"
const myName2: "Bob" = "Bobby"

myName๊ณผ myName2๋Š” ๋ชจ๋‘ "Bob" ์ž์ฒด๊ฐ€ ํƒ€์ž…์ธ๋ฐ ๊ฐ’์€ "Bobby"์ด๋ฏ€๋กœ, ์œ„ ์ฝ”๋“œ์—์„œ๋Š” ํƒ€์ž… ๋ถˆ์ผ์น˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

5-2. Unions ๐ŸŸข

๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์ด ๊ฐ’ ์ž์ฒด๊ฐ€ ํƒ€์ž…์ด ๋˜๋Š” ์ผ€์ด์Šค๋ฅผ ๋œปํ•œ๋‹ค๋ฉด, ์—ฌ๋Ÿฌ ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž… ์ค‘ ํ•˜๋‚˜๋งŒ ํ—ˆ์šฉ๋˜๋Š” ํƒ€์ž…์„ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

type User = {
    username: string
    role: "guest" | "member" | "admin"
}

type UserRole = "guest" | "member" | "admin"

let userRole: UserRole = "member"

5-3. Update order status to use literal type unions ๐ŸŸข

Order ํƒ€์ž…์˜ status ์†์„ฑ์— ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์ ์šฉํ•ฉ์‹œ๋‹ค.

type Order = {
  id: number;
  pizza: Pizza;
  status: "ordered" | "completed";
};

์ดํ›„ newOrder์— Order ํƒ€์ž…์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

function placeOrder(pizzaName: string) {
  const selectedPizza = menu.find((pizzaObj) => pizzaObj.name === pizzaName);

  if (!selectedPizza) {
    console.error(`${pizzaName} does not exist in the menu.`);
    return;
  }

  cashInRegister += selectedPizza.price;

  const newOrder: Order = {
    id: nextOrderId++,
    pizza: selectedPizza,
    status: "ordered",
  };

  orderQueue.push(newOrder);

  return newOrder;
}

order.status๋„ complete์—์„œ completed๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

function completeOrder(orderId: number) {
  const order = orderQueue.find((order) => order.id === orderId);

  if (!order) {
    console.error(`${orderId} was not found.`);
    return;
  }

  order.status = "completed";

  return order;
}

5-4. Add ids to pizzas ๐ŸŸข

์ด์ œ Pizza ํƒ€์ž…์— id๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

type Pizza = {
  id: number;
  name: string;
  price: number;
};

menu์— ๋ณ€๊ฒฝ๋œ Pizza ํƒ€์ž…์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

const menu: Pizza[] = [
  { name: "Margherita", price: 8 },
  { name: "Pepperoni", price: 10 },
  { name: "Hawaiian", price: 10 },
  { name: "Veggie", price: 9 },
];

id๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ๊ฒ ์ฃ .

const menu: Pizza[] = [
  { id: 1, name: "Margherita", price: 8 },
  { id: 2, name: "Pepperoni", price: 10 },
  { id: 3, name: "Hawaiian", price: 10 },
  { id: 4, name: "Veggie", price: 9 },
];

๋ฉ”๋‰ด๊ฐ€ ์ถ”๊ฐ€๋˜๋Š” ๋ถ€๋ถ„์—๋„ id๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

addNewPizza({ id: 5, name: "Chicken Bacon Ranch", price: 12 });
addNewPizza({ id: 6, name: "BBQ Chicken", price: 12 });
addNewPizza({ id: 7, name: "Spicy Sausage", price: 11 });

5-5. Type Narrowing ๐ŸŸข

์ด์ œ Type Narrowing์ด ๋ฌด์—‡์ธ์ง€ ์•Œ์•„๋ณด์ฃ .

getPizzaDetail() ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

function getPizzaDetail(identifier: string | number) {
  if (typeof identifier === "string") {
    return menu.find(
      (pizza) => pizza.name.toLowerCase() === identifier.toLowerCase()
    );
  } else {
    return menu.find((pizza) => pizza.id === identifier);
  }
}

Type Narrowing ์ด๋ž€ ์œ ๋‹ˆ์–ธ ํƒ€์ž… ๋“ฑ ๋„“์€ ๋ฒ”์œ„์˜ ํƒ€์ž…์—์„œ, ์‹ค์ œ ์ฝ”๋“œ ํ๋ฆ„์— ๋”ฐ๋ผ ๋” ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์œผ๋กœ "์ขํ˜€์„œ" ๋‹ค๋ฃจ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์œ„ ์ฝ”๋“œ๋Š” ์‹๋ณ„์ž์˜ ํƒ€์ž…์„ string ๋˜๋Š” number๋กœ ์ขํžŒ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

5-6. Be explicit whenever you can ๐ŸŸข

getPizzaDetail() ํ•จ์ˆ˜๋ฅผ ์กฐ๊ธˆ ๋” ๋””ํ…Œ์ผํ•˜๊ฒŒ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•ด๋‹น ํ•จ์ˆ˜๋กœ boolean ๊ฐ’์ด ๋“ค์–ด์˜ค๋ฉด TypeError๊ฐ€ ์ถœ๋ ฅ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

export function getPizzaDetail(identifier: string | number) {
  if (typeof identifier === "string") {
    return menu.find(
      (pizza) => pizza.name.toLowerCase() === identifier.toLowerCase()
    );
  } else if (typeof identifier === "number") {
    return menu.find((pizza) => pizza.id === identifier);
  } else {
    throw new TypeError(
      "Parameter 'identifier' must be either a string or number."
    );
  }
}

๐Ÿšจ ์ค‘๊ฐ„ ์š”์•ฝ
๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์ด ๊ฐ’ ์ž์ฒด๋ฅผ ํƒ€์ž…์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹, ์œ ๋‹ˆ์˜จ ํƒ€์ž…์ด ์—ฌ๋Ÿฌ ๊ฐ€๋Šฅํ•œ ๊ฐ’ ์ค‘ ํ•˜๋‚˜๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๋ฐฉ์‹(์˜ˆ: "ordered" | "completed")์ž„์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํƒ€์ž… ๋‚ด๋กœ์ž‰์„ ํ†ตํ•ด string ๋˜๋Š” number์™€ ๊ฐ™์€ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์—์„œ ์กฐ๊ฑด๋ฌธ์„ ํ™œ์šฉํ•ด ๋” ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์œผ๋กœ ์ขํ˜€ ์•ˆ์ „ํ•˜๊ฒŒ ์ž‘์—…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํฌํ•จํ•˜์—ฌ, ๋ช…์‹œ์ ์ธ ํƒ€์ž… ์ง€์ •์ด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๊ฐ•๋ ฅํ•œ ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜๋Š” ํ•ต์‹ฌ์ž„์„ ํ•™์Šตํ–ˆ์Šต๋‹ˆ๋‹ค.

6. Function Typing ๐ŸŒ€

6-1. Function return types ๐ŸŸข

type UserRole = "guest" | "member" | "admin";

type User = {
  username: string;
  role: UserRole;
};

const users: User[] = [
  { username: "john_doe", role: "member" },
  { username: "minkwan", role: "admin" },
  { username: "guest_user", role: "guest" },
];

function fetchUserDetails(username: string): User {
  const user = users.find((user) => user.username === username);
  if (!user) {
    throw new Error(`User with username ${username} not found.`);
  }
  return user;
}

: User ๋ถ€๋ถ„์ด ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” user์˜ ํƒ€์ž…์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

6-2. TS-specific types: any ๐ŸŸข

value์˜ type์„ any๋กœ ์ง€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

let value: any = 1;
value.toUpperCase();
value = "Hi";
value.map();

number์— ๋Œ€ํ•ด์„œ toUpperCase()๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ ค ํ•˜๊ณ , string์— ๋Œ€ํ•ด์„œ๋Š” map()์„ ์ ์šฉํ•˜๋ ค ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

any๋Š” ์“ฐ์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. TS์˜ ํƒ€์ž… ๊ฒ€์‚ฌ์˜ ์ด์ ์„ ๋ฌด๋ ฅํ™”ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋‹จ, JS๋ฅผ TS๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ณผ์ •์—์„œ ๋‹น์žฅ ๋ชจ๋“  ํƒ€์ž…์„ ์ •์˜ํ•˜๊ธฐ ์–ด๋ ค์šด ์ƒํ™ฉ์ด๋ผ๋ฉด ์ผ์‹œ์ ์œผ๋กœ TS ๊ฒ€์‚ฌ ์šฐํšŒ๋ฅผ ์œ„ํ•ด any๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์žˆ์Šต๋‹ˆ๋‹ค.

6-3. Add return type to getPizzaDetail ๐ŸŸข

ํ•จ์ˆ˜์˜ return type์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์—๋Š” ํฐ ์–ด๋ ค์›€์ด ์—†์Šต๋‹ˆ๋‹ค.

export function getPizzaDetail(identifier: string | number): Pizza | undefined {
  if (typeof identifier === "string") {
    return menu.find(
      (pizza) => pizza.name.toLowerCase() === identifier.toLowerCase()
    );
  } else if (typeof identifier === "number") {
    return menu.find((pizza) => pizza.id === identifier);
  } else {
    throw new TypeError(
      "Parameter 'identifier' must be either a string or number."
    );
  }
}

6-4. Void return type ๐ŸŸข

void๋Š” "์•„๋ฌด๊ฒƒ๋„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Œ"์„ ๋ช…์‹œ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๋Š” ํƒ€์ž…์ž…๋‹ˆ๋‹ค.

function addNewPizza(pizzaObj: Pizza): void {
  menu.push(pizzaObj);
}

์œ„ ํ•จ์ˆ˜๋Š” menu์— ์ƒˆ๋กœ์šด pizza๋ฅผ ์ถ”๊ฐ€ํ•  ๋ฟ ์–ด๋– ํ•œ ๊ฐ’๋„ '๋ฐ˜ํ™˜'ํ•˜์ง€ ์•Š๊ธฐ์— void ํƒ€์ž…์ด๋ผ๊ณ  ๋ช…์‹œํ•ด ์ค๋‹ˆ๋‹ค.

6-5. Add automatic ids to menu items ๐ŸŸข

id ์ž๋™ ์ฆ๊ฐ€๋ฅผ ์œ„ํ•ด ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

let nextPizzaId = 1;

id๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

function addNewPizza(pizzaObj: Pizza): void {
  pizzaObj.id = nextPizzaId++;
  menu.push(pizzaObj);
}

์ตœ์ข… ๋ฉ”๋‰ด์˜ ํ˜•ํƒœ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

const menu: Pizza[] = [
  { id: nextPizzaId++, name: "Margherita", price: 8 },
  { id: nextPizzaId++, name: "Pepperoni", price: 10 },
  { id: nextPizzaId++, name: "Hawaiian", price: 10 },
  { id: nextPizzaId++, name: "Veggie", price: 9 },
];

๐Ÿšจ ์ค‘๊ฐ„ ์š”์•ฝ
TS์˜ ํ•จ์ˆ˜์—๋Š” ๋ฐ˜ํ™˜ ํƒ€์ž…(User์™€ ๊ฐ™์€ ํŠน์ • ํƒ€์ž…์ด๋‚˜ Pizza | undefined์ฒ˜๋Ÿผ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜๊ฐ’ ์ง€์ •)์„ ๋ช…์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. any ํƒ€์ž…์˜ ์œ„ํ—˜์„ฑ(ํƒ€์ž… ๊ฒ€์‚ฌ๋ฅผ ๋ฌด๋ ฅํ™”ํ•˜๋ฏ€๋กœ ๊ฐ€๊ธ‰์  ์‚ฌ์šฉ ์ž์ œ)๊ณผ void ํƒ€์ž…(์•„๋ฌด๊ฒƒ๋„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž… ๋ช…์‹œ)์„ ์„ค๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ œ์‹œํ•œ ๊ฐœ๋…๋“ค์„ ํ™œ์šฉํ•ด ํ”ผ์ž ๋ฉ”๋‰ด ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์—์„œ ์‚ฌ์šฉ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ, ํ”ผ์ž ์ •๋ณด ์กฐํšŒ, ๋ฉ”๋‰ด ์•„์ดํ…œ ์ถ”๊ฐ€ ๋“ฑ์˜ ํ•จ์ˆ˜์— ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ๋ถ€์—ฌํ•˜๊ณ , ์ž๋™ ์ฆ๊ฐ€ํ•˜๋Š” ID๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ๋ช…์‹œ์ ์ธ ํƒ€์ž… ์ง€์ •์ด ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ๊ณผ ๊ฐ€๋…์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚จ ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

7. Advanced TypeScript Features ๐ŸŒ€

7-1. Utility Types & Partial ๐ŸŸข

Utility Types์€ TS๊ฐ€ ์ž์ฃผ ์“ฐ์ด๋Š” ํƒ€์ž… ๋ณ€ํ˜•์„ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ํƒ€์ž…์ž…๋‹ˆ๋‹ค.

๊ทธ์ค‘ Partial<T>๋Š” T ํƒ€์ž…์˜ ๋ชจ๋“  ์†์„ฑ์„ optional ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.

type User = {
    id: number
    username: string
    role: "member" | "contributor" | "admin"
}

type UpdatedUser = Partial<User>

const users: User[] = [
    { id: 1, username: "john_doe", role: "member" },
    { id: 2, username: "jane_smith", role: "contributor" },
    { id: 3, username: "alice_jones", role: "admin" },
    { id: 4, username: "charlie_brown", role: "member" },
];

function updateUser(id: number, updates: UpdatedUser) {
    // Find the user in the array by the id
    const foundUser = users.find(user => user.id === id)
    if (!foundUser) {
        console.error("User not found!")
        return
    }
    // Use Object.assign to update the found user in place. 
    Object.assign(foundUser, updates)
}

// Example updates:
updateUser(1, { username: "new_john_doe" });
updateUser(4, { role: "contributor" });

console.log(users)

type UpdatedUser = Partial<User> ๋ถ€๋ถ„์— ์˜ํ•ด User์˜ ๋ชจ๋“  ์†์„ฑ์ด optional ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ฆ‰, username ์†์„ฑ๋งŒ ํฌํ•จ๋œ ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•˜๋ฉด, ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ UpdatedUser์— ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ์ผ๋ถ€ ํ•„๋“œ์— ๋Œ€ํ•ด์„œ๋งŒ ์—…๋ฐ์ดํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

7-2. Omit Utility Type ๐ŸŸข

Omit<T, K>๋„ TS ๋‚ด์žฅ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

T ํƒ€์ž…์—์„œ K๋กœ ์ง€์ •ํ•œ ์†์„ฑ์„ ์ œ์™ธํ•œ ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

type User = {
    id: number
    username: string
    role: "member" | "contributor" | "admin"
}

type UpdatedUser = Partial<User>

let nextUserId = 1

const users: User[] = [
    { id: nextUserId++, username: "john_doe", role: "member" },
    { id: nextUserId++, username: "jane_smith", role: "contributor" }
];

function updateUser(id: number, updates: UpdatedUser) {
    const foundUser = users.find(user => user.id === id)
    if (!foundUser) {
        console.error("User not found!")
        return
    }
    Object.assign(foundUser, updates)
}

// updateUser(1, { username: "new_john_doe" });
// updateUser(4, { role: "contributor" });

function addNewUser(newUser: Omit<User, "id" | "user">): User {
    const user: User = {
        id: nextUserId++,
        ...newUser
    }
    users.push(user)
    return user
}

// example usage:
addNewUser({ username: "joe_schmoe", role: "member" })

console.log(users)

addNewUser() ํ•จ์ˆ˜๋Š” ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž๋ฅผ users ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ด๋•Œ Omit<User, "id" | "username">๋Š” User ํƒ€์ž…์—์„œ id์™€ username ์†์„ฑ์„ ์ œ์™ธํ•œ ํƒ€์ž…์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ฆ‰, ์ƒˆ๋กœ์šด ์‚ฌ์šฉ์ž๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ id์™€ username์„ ์ œ์™ธํ•œ ๊ฐ์ฒด๋งŒ ๋ฐ›๋„๋ก ์ œํ•œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋ถˆํ•„์š”ํ•œ ์†์„ฑ์„ ์ œ๊ฑฐํ•˜์—ฌ, ์œ ํšจํ•œ ์กฐ์ž‘๋งŒ ํ—ˆ์šฉํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

7-3. Fix TS warnings with Omit ๐ŸŸข

Pizza App์— Omit<T, K>์„ ์ ์šฉํ•˜๋ฉด, ์ฝ”๋“œ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

function addNewPizza(pizzaObj: Omit<Pizza, "id">): Pizza {
  const newPizza: Pizza = {
    id: nextPizzaId++,
    ...pizzaObj,
  };
  menu.push(newPizza);
  return newPizza;
}
addNewPizza({ name: "Chicken Bacon Ranch", price: 12 });
addNewPizza({ name: "BBQ Chicken", price: 12 });
addNewPizza({ name: "Spicy Sausage", price: 11 });

7-4. Generics ๐ŸŸข

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

const gameScores = [14, 21, 33, 42, 59]
const favoriteThings = ["raindrops on roses", "whiskers on kittens", "bright copper kettles", "warm woolen mittens"];
const voters = [{ name: "Alice", age: 42 }, { name: "Bob", age: 77 }]

function getLastItem<Type>(array: Type[]): Type | undefined {
    return array[array.length - 1]
}

console.log(getLastItem(gameScores))
console.log(getLastItem(favoriteThings))
console.log(getLastItem(voters))

์ฝ˜์†”์„ ๋ณด๋ฉด, getLastItem() ํ•จ์ˆ˜์— ๊ฐ๊ฐ ๋‹ค๋ฅธ ๋ฐฐ์—ด์„ ์ „๋‹ฌํ•˜๋Š” ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ๋ฐฐ์—ด์˜ ๋งˆ์ง€๋ง‰ ์š”์†Œ์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ํ˜•ํƒœ๋Š” ์œ„์™€ ๊ฐ™๊ณ , ์‹ค์ œ๋กœ ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์ „๋‹ฌํ•˜๋Š” ๊ณผ์ •์„ Pizza App ์ƒ์— ๊ตฌํ˜„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

7-5. Generic functions in the pizza restaurant ๐ŸŸข

Pizza App์— ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์ ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

function addToArray<T>(array: T[], item: T): T[] {
  array.push(item);
  return array;
}

์œ„ ํ•จ์ˆ˜๋Š” array์™€ item์„ ๋ฐ›๋Š”๋ฐ์š”, ์ด ๋‘˜์ด ๋™์ผํ•œ ํƒ€์ž…์ด๋ผ๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ T๋Š” ํŠน์ • ํƒ€์ž…์„ ๋‚˜ํƒ€๋‚ด๋Š”, ๋Œ€์ฒด ๊ฐ€๋Šฅํ•œ ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค.

addToArray(menu, { id: nextPizzaId++, name: "Chicken Bacon Ranch", price: 12 });
addToArray(orderQueue, { id: nextOrderId++, pizza: menu[2], status: "done" });

menu์˜ ํƒ€์ž…์€ Pizza[]์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ T๋Š” Pizza๋กœ ์ž๋™ ์ถ”๋ก ๋ฉ๋‹ˆ๋‹ค.

orderQueue์˜ ํƒ€์ž…์€ Order[]์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ T๋Š” Order๋กœ ์ถ”๋ก ๋˜์ฃ .

7-6. Explicitly type generic function calls ๐ŸŸข

๋ช…์‹œ์ ์œผ๋กœ ์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜๋ฉด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.

addToArray<Pizza>(menu, { id: nextPizzaId++, name: "Chicken Bacon Ranch", price: 12 })
addToArray<Order>(orderQueue, { id: nextOrderId++, pizza: menu[2], status: "completed" })

์ตœ์ข…์ ์œผ๋กœ ์™„์„ฑ๋œ Pizza App์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

type Pizza = {
  id: number;
  name: string;
  price: number;
};

type Order = {
  id: number;
  pizza: Pizza;
  status: "ordered" | "completed";
};

let cashInRegister = 100;
let nextOrderId = 1;
let nextPizzaId = 1;

const menu: Pizza[] = [
  { id: nextPizzaId++, name: "Margherita", price: 8 },
  { id: nextPizzaId++, name: "Pepperoni", price: 10 },
  { id: nextPizzaId++, name: "Hawaiian", price: 10 },
  { id: nextPizzaId++, name: "Veggie", price: 9 },
];

const orderQueue: Order[] = [];

function addNewPizza(pizzaObj: Pizza): Pizza {
  menu.push(pizzaObj);
  return pizzaObj;
}

function placeOrder(pizza: Pizza): Order | undefined {
  const newOrder: Order = {
    id: nextOrderId++,
    pizza: pizza,
    status: "ordered",
  };
  orderQueue.push(newOrder);
  cashInRegister += pizza.price;
  return newOrder;
}

function addToArray<T>(array: T[], item: T): T[] {
  array.push(item);
  return array;
}

addToArray<Pizza>(menu, {
  id: nextPizzaId++,
  name: "Chicken Bacon Ranch",
  price: 12,
});
addToArray<Order>(orderQueue, {
  id: nextOrderId++,
  pizza: menu[2],
  status: "completed",
});

console.log(menu);
console.log(orderQueue);

function completeOrder(orderId: number): Order | undefined {
  const order = orderQueue.find((order) => order.id === orderId);
  if (!order) {
    console.error(`${orderId} was not found in the orderQueue`);
    return;
  }
  order.status = "completed";
  return order;
}

export function getPizzaDetail(identifier: string | number): Pizza | undefined {
  if (typeof identifier === "string") {
    return menu.find(
      (pizza) => pizza.name.toLowerCase() === identifier.toLowerCase()
    );
  } else if (typeof identifier === "number") {
    return menu.find((pizza) => pizza.id === identifier);
  } else {
    throw new TypeError(
      "Parameter `identifier` must be either a string or a number"
    );
  }
}

๐Ÿšจ ์ค‘๊ฐ„ ์š”์•ฝ
TS์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์ธ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…๊ณผ ์ œ๋„ค๋ฆญ์— ๊ด€ํ•œ ๋‚ด์šฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค. Partial<T>๋Š” ํƒ€์ž…์˜ ๋ชจ๋“  ์†์„ฑ์„ ์„ ํƒ์ ์œผ๋กœ ๋งŒ๋“ค์–ด ์ผ๋ถ€ ํ•„๋“œ๋งŒ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. Omit<T, K>๋Š” ํŠน์ • ์†์„ฑ์„ ์ œ์™ธํ•œ ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ์ƒ์„ฑํ•˜์ฃ . ๋์œผ๋กœ, ์ œ๋„ค๋ฆญ์€ ํ•จ์ˆ˜๋‚˜ ํด๋ž˜์Šค๊ฐ€ ๋‹ค์–‘ํ•œ ํƒ€์ž…์—์„œ ์žฌ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋„๋ก ํƒ€์ž… ๋ณ€์ˆ˜๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ์œผ๋กœ, ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์œ ์—ฐ์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

8. Conclusion ๐ŸŒ€

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ํ•™์Šตํ•˜๋ฉด์„œ ๋งŽ์€ ๋‚ด์šฉ์„ ์ตํ˜”์ง€๋งŒ, ์•„์ง ๋‹ค๋ฃจ์ง€ ๋ชปํ•œ ๋ถ€๋ถ„๋„ ๋งŽ์•„ ์ถ”๊ฐ€์ ์ธ ํ•™์Šต์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์ ์„ ๋А๊ผˆ์Šต๋‹ˆ๋‹ค.

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์ฝ”๋“œ์˜ ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์‚ดํŽด๋ณธ ๊ธฐ๋Šฅ๋“ค์„ ๋ฐ˜๋“œ์‹œ ๋ชจ๋‘ ์ ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ธฐ์ˆ  ์ž์ฒด๊ฐ€ ๋ชฉ์ ์ด ๋˜์–ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค.

์ค‘์š”ํ•œ ๊ฒƒ์€ ํ”„๋กœ์ ํŠธ์˜ ํŠน์„ฑ๊ณผ ์ƒํ™ฉ์— ๋งž๊ฒŒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ ์ ˆํžˆ ํ™œ์šฉํ•จ์œผ๋กœ์จ ์ „์ฒด์ ์ธ ์•ˆ์ •์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

profile
Write a little every day, without hope, without despair โœ๏ธ

0๊ฐœ์˜ ๋Œ“๊ธ€