# ๐Ÿ“„ Item24 ~ 28


# Item24. ์ผ๊ด€์„ฑ ์žˆ๋Š” ๋ณ„์นญ ์‚ฌ์šฉํ•˜๊ธฐ

  • ๋ณ„์นญ์€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํƒ€์ž…์„ ์ขํžˆ๋Š” ๊ฒƒ์„ ๋ฐฉํ•ดํ•œ๋‹ค.
    • ๋ณ„์นญ์„ ๋‚จ๋ฐœํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์ œ์–ด ํ๋ฆ„์„ ๋ถ„์„ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
    • ๋”ฐ๋ผ์„œ ๋ณ€์ˆ˜์— ๋ณ„์นญ์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ผ๊ด€๋˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
const borough = {
  name: 'Brooklyn',
  location: [40.688, -73.979],
};

const loc = borough.location;

loc[0] = 0;
console.log(borough.location); // [0, -73.979]
interface Coordinate {
  x: number;
  y: number;
}

interface BoundingBox {
  x: [number, number];
  y: [number, number];
}

interface Polygon {
  exterior: Coordinate[];
  holes: Coordinates[][];
  bbox?: BoundingBox;
}

// before
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  if (polygon.bbox) {
    if (pt.x < polygon.bbox.x[0] || pt.x > polygon.bbox.x[1] || pt.y < polygon.bbox.y[0] || pt.y > polygon.bbox.y[1]) {
      return false;
    }
  }
}

// after - ํƒ€์ž… ์˜ค๋ฅ˜ ๋ฐœ์ƒ(strictNullChecks ํ™œ์„ฑํ™” ์ƒํƒœ)
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  const box = polygon.bbox; // ํƒ€์ž…์ด BoundingBox | undefined
  if (polygon.bbox) {
    if (pt.x < box.x[0] || pt.x > box.x[1] || pt.y < box.y[0] || pt.y > box.y[1]) {
      return false;
    }
  }
}
// ์œ„์™€ ๊ฐ™์€ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ ์ด์œ ๋Š” ์†์„ฑ ์ฒดํฌ๋Š” polygon.bbox์˜ ํƒ€์ž…์„ ์ •์ œํ–ˆ์ง€๋งŒ box๋Š” ๊ทธ๋ ‡์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
// ์ด๋Ÿฌํ•œ ์˜ค๋ฅ˜๋Š” "๋ณ„์นญ์„ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์‚ฌ์šฉํ•œ๋‹ค"๋Š” ๊ธฐ๋ณธ ์›์น™์„ ์ง€ํ‚ค๋ฉด ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  const box = polygon.bbox; // ํƒ€์ž…์ด BoundingBox | undefined
  if (box) {
    if (pt.x < box.x[0] || pt.x > box.x[1] || pt.y < box.y[0] || pt.y > box.y[1]) {
      return false;
    }
  }
}

  • ์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ๋Š” ๊ฐ์ฒด ๋น„๊ตฌ์กฐํ™”๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ณด๋‹ค ๊ฐ„๊ฒฐํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ์ผ๊ด€๋œ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
  const { bbox } = polygon;
  if (bbox) {
    const {x, y} = bbox;
    if (pt.x < x[0] || pt.x > x[1] || pt.y < y[0] || pt.y > y[1]) {
      return false;
    }
  }
}
  • ๊ฐ์ฒด ๋น„๊ตฌ์กฐํ™” ์ด์šฉ์‹œ ์ฃผ์˜์‚ฌํ•ญ
    • ์ „์ฒด bbox ์†์„ฑ์ด ์•„๋‹ˆ๋ผ x, y๊ฐ€ ์„ ํƒ์  ์†์„ฑ์ผ ๊ฒฝ์šฐ ์†์„ฑ ์ฒดํฌ๊ฐ€ ๋” ํ•„์š”ํ•˜๋‹ค.
      • ๋”ฐ๋ผ์„œ ํƒ€์ž…์˜ ๊ฒฝ๊ณ„์— null ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
    • bbox์—๋Š” ์„ ํƒ์  ์†์„ฑ์ด ์ ํ•ฉํ–ˆ์ง€๋งŒ holes๋Š” ๊ทธ๋ ‡์ง€ ์•Š๋‹ค.
      • holes๊ฐ€ ์„ ํƒ์ ์ด๋ผ๋ฉด, ๊ฐ’์ด ์—†๊ฑฐ๋‚˜ ๋นˆ ๋ฐฐ์—ด์ด์—ˆ์„ ๊ฒƒ์ด๋‹ค.
      • ์ฐจ์ด๊ฐ€ ์—†๋Š”๋ฐ ์ด๋ฆ„์„ ๊ตฌ๋ณ„ํ•œ ๊ฒƒ์ด๋‹ค.
      • ๋นˆ ๋ฐฐ์—ด์€ 'holes ์—†์Œ'์„ ๋‚˜ํƒ€๋‚ด๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‹ค.

  • ๋ณ„์นญ์€ ํƒ€์ž… ์ฒด์ปค๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋Ÿฐํƒ€์ž„์—๋„ ํ˜ผ๋™์„ ์•ผ๊ธฐํ•  ์ˆ˜ ์žˆ๋‹ค.
const { bbox } = polygon;
if (!bbox) {
  calculatePolygonBbox(polygon); // polygon.bbox๊ฐ€ ์ฑ„์›Œ์ง„๋‹ค.
  // ์ด์ œ polygon.bbox์™€ bbox๋Š” ๋‹ค๋ฅธ ๊ฐ’์„ ์ฐธ์กฐํ•œ๋‹ค.
}
  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ์ œ์–ด ํ๋ฆ„ ๋ถ„์„์€ ์ง€์—ญ ๋ณ€์ˆ˜์—๋Š” ๊ฝค ์ž˜ ๋™์ž‘ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฐ์ฒด ์†์„ฑ์—์„œ๋Š” ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.
function fn(p: Polygon) { /* ... */ }

polygon.bbox // ํƒ€์ž…์ด BoundingBox | undefined

if (polygon.bbox) {
  polygon.bbox // ํƒ€์ž…์ด BoundingBox
  fn(polygon);
  polygon.bbox // ํƒ€์ž…์ด BoundingBox
}
  • fn(polygon); ํ˜ธ์ถœ์€ polygon.bbox๋ฅผ ์ œ๊ฑฐํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋ฏ€๋กœ ํƒ€์ž…์„ BoundingBox | undefined ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•  ๊ฒƒ์ด๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์†์„ฑ ์ฒดํฌ๋ฅผ ๋ฐ˜๋ณตํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ข‹์ง€์•Š๋‹ค.
    • ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด ๊ฐ์ฒด ์†์„ฑ์˜ ํƒ€์ž… ์ •์ œ๋ฅผ ๋ฌดํšจํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค. ์†์„ฑ๋ณด๋‹ค ์ง€์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํƒ€์ž… ์ •์ œ๋ฅผ ๋ฏฟ์„ ์ˆ˜ ์žˆ๋‹ค.
    • polygon.bbox๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  bbox ์ง€์—ญ ๋ณ€์ˆ˜๋กœ ๋ฝ‘์•„๋‚ด์„œ ์‚ฌ์šฉํ•˜๋ฉด bbox ํƒ€์ž…์€ ์ •ํ™•ํžˆ ์œ ์ง€๋˜๋งŒ, polygon.bbox์˜ ๊ฐ’๊ณผ ๊ฐ™๊ฒŒ ์œ ์ง€๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค.

# Item25. ๋น„๋™๊ธฐ ์ฝ”๋“œ์—๋Š” ์ฝœ๋ฐฑ ๋Œ€์‹  async ํ•จ์ˆ˜ ์‚ฌ์šฉํ•˜๊ธฐ

  • ์ฝœ๋ฐฑ๋ณด๋‹ค๋Š” ํ”„๋กœ๋ฏธ์Šค๋‚˜ async/await๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ 
    • ์ฝœ๋ฐฑ๋ณด๋‹ค๋Š” ํ”„๋กœ๋ฏธ์Šค๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์‰ฝ๋‹ค.
    • ์ฝœ๋ฐฑ๋ณด๋‹ค๋Š” ํ”„๋กœ๋ฏธ์Šค๊ฐ€ ํƒ€์ž…์„ ์ถ”๋ก ํ•˜๊ธฐ ์‰ฝ๋‹ค.

  • ์˜ˆ์‹œ1) ๋ณ‘๋ ฌ๋กœ ํŽ˜์ด์ง€ ๋กœ๋“œ
async function fetchPages() {
  const [response1, response2, response3] = await Promise.all([
    fetch(url1), fetch(url2), fetch(url3)
  ]);
}
  • ์˜ˆ์‹œ2) Promise.race๋กœ ํƒ€์ž„์•„์›ƒ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•
    • Promise.race์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์€ ์ž…๋ ฅ ํƒ€์ž…๋“ค์˜ ์œ ๋‹ˆ์˜จ์ด๊ณ , ์ด๋ฒˆ ๊ฒฝ์šฐ๋Š” Promise<Response | never>๊ฐ€ ๋˜๋‚˜ never์™€์˜ ์œ ๋‹ˆ์˜จ์€ ์•„๋ฌด๋Ÿฐ ํšจ๊ณผ๊ฐ€ ์—†์œผ๋ฏ€๋กœ Promise<Response>๋กœ ๊ฐ„๋‹จํ•ด์ง„๋‹ค.
function timeout(millis: number): Promise<never> {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject('timeout'), millis);
  });
}

async function fetchWithTimeout(url: string, ms: number) {
  return Promise.race([fetch(url), timeout(ms)]);
}

  • ๊ฐ€๋” ํ”„๋กœ๋ฏธ์Šค๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•ด์•ผํ•  ๋•Œ ์ฝœ๋ฐฑ API๋ฅผ ๋ž˜ํ•‘ํ•  ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋Š”๋ฐ ์„ ํƒ์˜ ์—ฌ์ง€๊ฐ€ ์žˆ๋‹ค๋ฉด ์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ํ”„๋กœ๋ฏธ์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋ณด๋‹ค๋Š” async/await๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
    • ์ผ๋ฐ˜์ ์œผ๋กœ ๋” ๊ฐ„๊ฒฐํ•˜๊ณ  ์ง๊ด€์ ์ธ ์ฝ”๋“œ๊ฐ€ ๋œ๋‹ค.
    • async ํ•จ์ˆ˜๋Š” ํ•ญ์ƒ ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ฐ•์ œ๋œ๋‹ค.
// function getNumber(): Promise<number>
async function getNumber() {
  return 42;
}

const getNumber = async () => 42; // ํƒ€์ž…์ด () => Promise<number>

const getNumber = () => Promise.resolve(42); // ํƒ€์ž…์ด () => Promise<number>
  • async ํ•จ์ˆ˜์—์„œ ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋˜ ๋‹ค๋ฅธ ํ”„๋กœ๋ฏธ์Šค๋กœ ๋ž˜ํ•‘๋˜์ง€ ์•Š๋Š”๋‹ค.
    • ๋ฐ˜ํ™˜ ํƒ€์ž…์€ Promise<Promise<T>>๊ฐ€ ์•„๋‹Œ Promise<T>๊ฐ€ ๋œ๋‹ค.
    • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํƒ€์ž… ์ •๋ณด๊ฐ€ ๋ช…ํ™•ํžˆ ๋“œ๋Ÿฌ๋‚˜๋ฏ€๋กœ ๋น„๋™๊ธฐ ์ฝ”๋“œ์˜ ๊ฐœ๋…์„ ์žก๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค.
// function getJSON(url: string): Promise<any>
async function getJSON(url: string) {
  const response = await fetch(url);
  const jsonPromise = response.json(); // ํƒ€์ž…์ด Promise<any>
  return jsonPromise;
}

# Item26. ํƒ€์ž… ์ถ”๋ก ์— ๋ฌธ๋งฅ์ด ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉ๋˜๋Š”์ง€ ์ดํ•ดํ•˜๊ธฐ

type Language = 'JavaScript' | 'TypeScript' | 'Python';
function setLanguage(language: Language) { /* ... */ }

// ์ธ๋ผ์ธ ํ˜•ํƒœ
setLanguage('JavaScript'); // ์ •์ƒ

// ์ฐธ์กฐ ํ˜•ํƒœ
let language = 'JavaScript';
setLanguage(language); // 'string' ํ˜•์‹์˜ ์ธ์ˆ˜๋Š” 'Language' ํ˜•์‹์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ• ๋‹น๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ์œ„ ์ฝ”๋“œ์—์„œ ์ธ๋ผ์ธ ํ˜•ํƒœ์—์„œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ํ•จ์ˆ˜ ์„ ์–ธ์„ ํ†ตํ•ด ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ Language ํƒ€์ž…์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ๋‹ค.
    • ํ•ด๋‹น ํƒ€์ž…์— ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด 'JavaScript'๋Š” ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์ •์ƒ์ด๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ ์ด ๊ฐ’์„ ๋ณ€์ˆ˜๋กœ ๋ถ„๋ฆฌํ•ด๋‚ด๋ฉด, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ํ• ๋‹น ์‹œ์ ์— ํƒ€์ž…์„ ์ถ”๋ก ํ•œ๋‹ค.
  • ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์œผ๋กœ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.
  • ์ฒซ ๋ฒˆ์งธ ํ•ด๋ฒ•์€ ํƒ€์ž… ์„ ์–ธ์—์„œ language์˜ ๊ฐ€๋Šฅํ•œ ๊ฐ’์„ ์ œํ•œํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
let language: Language = 'JavaScript';
setLanguage(language);
  • ๋‘ ๋ฒˆ์งธ ํ•ด๋ฒ•์€ language๋ฅผ ์ƒ์ˆ˜๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.
const language = 'JavaScript';
setLanguage(language);
  • ์œ„์™€ ๊ฐ™์€ ๊ณผ์ •์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ฌธ๋งฅ์œผ๋กœ๋ถ€ํ„ฐ ๊ฐ’์„ ๋ถ„๋ฆฌํ–ˆ๋‹ค.
    • ๋ฌธ๋งฅ๊ณผ ๊ฐ’์„ ๋ถ„๋ฆฌํ•˜๋ฉด ์ถ”ํ›„์— ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
    • ์ด์ œ๋ถ€ํ„ฐ ์ด๋Ÿฌํ•œ ๋ฌธ๋งฅ์˜ ์†Œ์‹ค๋กœ ์ธํ•ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ฒฝ์šฐ์™€, ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด์ž.

# โญ๏ธ ํŠœํ”Œ ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ 

function panTo(where: [number, number]) { /* ... */ }

panTo([10, 20]); // ์ •์ƒ

const loc = [10, 20];
panTo(loc); // 'number[]' ํ˜•์‹์˜ ์ธ์ˆ˜๋Š” '[number, number]' ํ˜•์‹์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ• ๋‹น๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ํƒ€์ž… ์„ ์–ธ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•
const loc: [number, number] = [10, 20];
panTo(loc);
  • '์ƒ์ˆ˜ ๋ฌธ๋งฅ' ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•
    • const : ๋‹จ์ง€ ๊ฐ’์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ์ฐธ์กฐ๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ์–•์€ ์ƒ์ˆ˜
    • as const : ๊ทธ ๊ฐ’์ด ๋‚ด๋ถ€๊นŒ์ง€ ์ƒ์ˆ˜๋ผ๋Š” ์‚ฌ์‹ค์„ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—๊ฒŒ ์•Œ๋ฆผ
const loc = [10, 20] as const;
panTo(loc); // ~~~ 'readonly [10, 20]' ํ˜•์‹์€ 'readonly' ์ด๋ฉฐ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ํ˜•์‹ '[number, number]'์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ์œ„ ์˜ค๋ฅ˜๋Š” ์ •ํ™•ํžˆ ์ถ”๋ก ํ•œ ๋‚ด์šฉ์ด๋‹ค.
    • panTo์˜ ํƒ€์ž… ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” where์˜ ๋‚ด์šฉ์ด ๋ถˆ๋ณ€์ด๋ผ๊ณ  ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.
    • ์ฆ‰, loc ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ readonly ํƒ€์ž…์ด๋ฏ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ์ตœ์„ ์˜ ํ•ด๊ฒฐ์ฑ… : ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ํƒ€์ž…์— readonly ์ง€์ •
function panTo(where: readonly [number, number]) { /* ... */ }

const loc = [10, 20] as const;
panTo(loc);
  • as const์˜ ๋‹จ์ 
    • ๋งŒ์•ฝ ํƒ€์ž… ์ •์˜์— ์‹ค์ˆ˜๊ฐ€ ์žˆ๋‹ค๋ฉด (ex. ํŠœํ”Œ์— ์„ธ ๋ฒˆ์งธ ์š”์†Œ ์ถ”๊ฐ€) ์˜ค๋ฅ˜๋Š” ํƒ€์ž… ์ •์˜๊ฐ€ ์•„๋‹ˆ๋ผ ํ˜ธ์ถœ๋˜๋Š” ๊ณณ์—์„œ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
    • ํŠนํžˆ ์—ฌ๋Ÿฌ ๊ฒน ์ค‘์ฒฉ๋œ ๊ฐ์ฒด์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ๊ทผ๋ณธ์ ์ธ ์›์ธ์„ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
const loc = [10, 20, 30] as const;
panTo(loc); // 'readonly [10, 20, 30]' ํ˜•์‹์˜ ์ธ์ˆ˜๋Š” 'readonly [number, number]' ํ˜•์‹์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ• ๋‹น๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
            // 'length' ์†์„ฑ์˜ ํ˜•์‹์ด ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
            // '3' ํ˜•์‹์€ '2' ํ˜•์‹์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

# โญ๏ธ ๊ฐ์ฒด ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ 

type Language = 'JavaScript' | 'TypeScript' | 'Python';

interface GovernedLanguage {
  language: Language;
  organization: string;
}

function complain(language: GovernedLanguage) { /* ... */ }

complain({ language: 'TypeScript', organization: 'Microsoft' }); // ์ •์ƒ

const ts = {
  language: 'TypeScript',
  organization: 'Microsoft',
};

complain(ts); // ~~'{ language: string; organization: string; }' ํ˜•์‹์˜ ์ธ์ˆ˜๋Š” 'GovernedLanguage' ํ˜•์‹์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— ํ• ๋‹น๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
              // 'language' ์†์„ฑ์˜ ํ˜•์‹์ด ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
              // 'string' ํ˜•์‹์€ 'Language' ํ˜•์‹์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ts ๊ฐ์ฒด์—์„œ language์˜ ํƒ€์ž…์€ string์œผ๋กœ ์ถ”๋ก ๋œ๋‹ค.
    • ์ด ๋ฌธ์ œ๋Š” ํƒ€์ž… ์„ ์–ธ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜(const ts: GovernedLanguage = ...) ์ƒ์ˆ˜ ๋‹จ์–ธ(as const)์„ ์‚ฌ์šฉํ•ด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

# โญ๏ธ ์ฝœ๋ฐฑ ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ 

function callWithRandomNumbers(fn: (n1: number, n2: number) => void) {
  fn(Math.random(), Math.random());
}

callWithRandomNumbers((a, b) => {
  a; // ํƒ€์ž…์ด number
  b; // ํƒ€์ž…์ด number
    
  console.log(a + b);
})
  • callWithRandomNumbers ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ๋“ค์–ด๊ฐ€๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์ƒ์ˆ˜๋กœ ๋ฝ‘์•„๋‚ด๋ฉด ๋ฌธ๋งฅ์ด ์†Œ์‹ค๋˜๊ณ  noImplicitAny ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.
  • ์ด๋Ÿฐ ๊ฒฝ์šฐ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜์— ํƒ€์ž… ๊ตฌ๋ฌธ์„ ์ถ”๊ฐ€ํ•ด์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜๋Š” ๊ฐ€๋Šฅํ•  ๊ฒฝ์šฐ ์ „์ฒด ํ•จ์ˆ˜ ํ‘œํ˜„์‹์— ํƒ€์ž… ์„ ์–ธ์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
const fn = (a: number, b: number) => {
  console.log(a + b);
}

callWithRandomNumbers(fn);

# Item27. ํ•จ์ˆ˜ํ˜• ๊ธฐ๋ฒ•๊ณผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ํƒ€์ž… ํ๋ฆ„ ์œ ์ง€ํ•˜๊ธฐ

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

# Item28. ์œ ํšจํ•œ ์ƒํƒœ๋งŒ ํ‘œํ˜„ํ•˜๋Š” ํƒ€์ž…์„ ์ง€ํ–ฅํ•˜๊ธฐ

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

# โญ๏ธ [์˜ˆ์‹œ] ํŽ˜์ด์ง€ ๋‚ด์šฉ ๋กœ๋“œ ํ›„ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๋Š” ์ฝ”๋“œ

# ๐Ÿ“ before

interface State {
  pageText: string;
  isLoading: boolean;
  error?: string;
}
function renderPage(state: State) {
  if (state.error) {
    return `Error! Unable to load ${currentPage}: ${state.error}`;
  } else if (state.isLoading) {
    return `Loading ${currentPage}...`;
  }
    
  return `<h1>${currentPage}</h1>\n${state.pageText}`;
}
async function changePage(state: State, newPage: string) {
  state.isLoading = true;
    
  try {
    const response = await fetch(getUrlForPage(newPage));
      
    if (!response.ok) {
      throw new Error(`Unable to load ${newPage}: ${response.statusText}`);
    }
      
    const text = await response.text();
    state.isLoading = false;
    state.pageText = text;
  } catch (e) {
    state.error = '' + e;
  }
}
  • ์œ„ ํ•จ์ˆ˜๋“ค์€ ๋ฌธ์ œ์ ์ด ์กด์žฌํ•œ๋‹ค.
    • ์ƒํƒœ ๊ฐ’์˜ ๋‘ ๊ฐ€์ง€ ์†์„ฑ์ด ๋™์‹œ์— ์ •๋ณด๊ฐ€ ๋ถ€์กฑํ•˜๊ฑฐ๋‚˜(์š”์ฒญ์ด ์‹คํŒจํ•œ ๊ฒƒ์ธ์ง€ ์—ฌ์ „ํžˆ ๋กœ๋”ฉ ์ค‘์ธ์ง€ ์•Œ ์ˆ˜ ์—†์Œ), ๋‘ ๊ฐ€์ง€ ์†์„ฑ์ด ์ถฉ๋Œ(์˜ค๋ฅ˜์ด๋ฉด์„œ ๋™์‹œ์— ๋กœ๋”ฉ ์ค‘์ผ ์ˆ˜ ์žˆ์Œ)ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • State ํƒ€์ž…์€ isLoading์ด true ์ด๊ณ  ๋™์‹œ์— error ๊ฐ’์ด ์กด์žฌํ•˜๋Š” ๋ฌดํšจํ•œ ์ƒํƒœ๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค.
    • ๋ฌดํšจํ•œ ์ƒํƒœ๊ฐ€ ์กด์žฌํ•˜๋ฉด ์œ„ ๋‘ ํ•จ์ˆ˜๋ฅผ ์ œ๋Œ€๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์—†๋‹ค.

# ๐Ÿ“ after

  • ๋„คํŠธ์›Œํฌ ์š”์ฒญ ๊ณผ์ • ๊ฐ๊ฐ์˜ ์ƒํƒœ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋ชจ๋ธ๋ฆฌํ•˜๋Š” ํƒœ๊ทธ๋œ ์œ ๋‹ˆ์˜จ์„ ์‚ฌ์šฉ
interface RequestPending {
  state: 'pending';
}

interface RequestError {
  state: 'error';
  error: string;
}

interface RequestSuccess {
  state: 'ok';
  pageText: string;
}

type RequestState = RequestPending | RequestError | RequestSuccess;

interface State {
  currentPage: string;
  request: { [page: string]: RequestState };
}
function renderPage(state: State) {
  const { currentPage } = state;
  const requestState = state.requests[currentPage];
  
  switch (requestState.state) {
    case 'pending':
      return `Loading ${currentPage}...`;
    case 'error':
      return `Error! Unable to load ${currentPage}: ${requestState.error}`;
    case 'ok':
      return `<h1>${currentPage}</h1>\n${state.pageText}`;
  }
}
async function changePage(state: State, newPage: string) {
  state.requests[newPage] = { state: 'pending' };
  state.currentPage = newPage;
    
  try {
    const response = await fetch(getUrlForPage(newPage));
      
    if (!response.ok) {
      throw new Error(`Unable to load ${newPage}: ${response.statusText}`);
    }
      
    const text = await response.text();
    state.requests[newPage] = { state: 'ok', pageText };
  } catch (e) {
    state.requests[newPage] = { state: 'error', error: '' + e };
  }
}