2024-06-19.md

๐Ÿก

DIL: ์ดํŽ™ํ‹ฐ๋ธŒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ

์Šคํ„ฐ๋””: ์›”๊ฐ„ CS, https://github.com/monthly-cs/2024-05-effective-typescript
์ž‘์„ฑ์ผ: 2024-06-19
์ž‘์„ฑ์ž: dusunax


์•„์ดํ…œ 54: ๊ฐ์ฒด๋ฅผ ์ˆœํšŒํ•˜๋Š” ๋…ธํ•˜์šฐ Know How to Iterate Over Objects

const obj = {
  one: "uno",
  two: "dos",
  three: "tres",
};
for (const k in obj) {
  // const k: string ๊ฐ์ฒด๋ฅผ ์ˆœํšŒํ•˜๋Š” ๋ฃจํ”„ ๋‚ด์˜ ์ƒ์ˆ˜ k๋Š” string
  const v = obj[k]; // obj ๊ฐ์ฒด์—๋Š” ์„ธ ๊ฐœ์˜ key๋งŒ ์กด์žฌ (k์™€ obj ๊ฐ์ฒด์˜ ํ‚ค ํƒ€์ž…์ด ์„œ๋กœ ๋‹ค๋ฅด๊ฒŒ ์ถ”๋ก )
  //        ~~~~~~ Element implicitly has an 'any' type
  //               because type ... has no index signature
}

let k: keyof typeof obj;
  • k์˜ ํƒ€์ž…์ด "one", "two", "three"๊ฐ€ ์•„๋‹ˆ๋ผ string์œผ๋กœ ์ถ”๋ก ๋˜๋Š” ์ด์œ ?
interface ABC {
  a: string;
  b: string;
  c: number;
}

function foo(abc: ABC) {
  for (const k in abc) {
    //       ^? const k: string
    const v = abc[k];
    //        ~~~~~~ Element implicitly has an 'any' type
    //               because type 'ABC' has no index signature
  }
}

const x = { a: "a", b: "b", c: 2, d: new Date() }; // ABC์™€ ํ˜ธํ™˜๋˜๋Š” ๊ฐ์ฒด
foo(x); // ๊ตฌ์กฐ์  ํƒ€์ดํ•‘ OK
// ABC ํƒ€์ž…์— 'ํ• ๋‹น ๊ฐ€๋Šฅํ•œ " ์–ด๋–ค ๊ฐ’์ด๋“  ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํ—ˆ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ
// ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ABC ํƒ€์ž…์˜ ํ‚ค๋ฅผ string์œผ๋กœ ์„ ํƒํ•œ๋‹ค.

// ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์•„๋ž˜์˜ ๊ฒฝ์šฐ, v๋ฅผ string | number๋กœ๋งŒ ์ถ”๋ก ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์Œ
function foo(abc: ABC) {
  const keys = ["a", "b", "c"] as const;
  for (const k of keys) {
    //       ^? const k: "a" | "b" | "c"
    const v = abc[k];
    //    ^? const v: string | number
  }
}
  • ๋‹จ์ˆœํžˆ ๊ฐ์ฒด์˜ ํ‚ค์™€ ๊ฐ’์„ ์ˆœํšŒํ•œ๋‹ค๋ฉด? Object.entries

ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ

Object.prototype.z = 3;
  • for-in ๋ฃจํ”„์—์„œ k๊ฐ€ string์ด๋ผ๋ฉด? ํ”„๋กœํ† ํƒ€์ž„ ์˜ค์—ผ ๊ฐ€๋Šฅ์„ฑ
  • keyof ์„ ์–ธ์€ ์ƒ์ˆ˜๊ฑฐ๋‚˜, ์ถ”๊ฐ€์ ์ธ ํ‚ค ์—†์ด ์ •ํ™•ํ•œ ํƒ€์ž…์„ ์›ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ ์ ˆํ•จ

Map

  • Map์€ ๋ฐ˜๋ณต(iteration)์„ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•จ
    • forEach, entries, keys, values
    • Map์€ ํ•ญ๋ชฉ์ด ์‚ฝ์ž…๋œ ์ˆœ์„œ๋ฅผ ๊ธฐ์–ตํ•˜๊ณ  ๊ทธ ์ˆœ์„œ๋ฅผ ์œ ์ง€ => ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•œ ์ˆœ์„œ๋Œ€๋กœ ๋ฐ˜๋ณตํ•  ์ˆ˜ ์žˆ์Œ
  • ๊ฐ์ฒด๋ฅผ ๋ฐ˜๋ณตํ•˜๋ ค๋ฉด Object.keys, Object.values, Object.entries์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•จ (๊ฐ์ฒด๋ณด๋‹ค Map์ด ๋” ์ง๊ด€์ )
const m = new Map([
  ["one", "uno"],
  ["two", "dos"],
  ["three", "tres"],
]);
//  ^? const m: Map<string, string>

// ๋ชจ๋“  ํ•ญ๋ชฉ์„ ์ˆœํšŒ
m.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});

// entries() ์‚ฌ์šฉ
for (const [key, value] of m.entries()) {
  console.log(`${key}: ${value}`);
}

// keys() ์‚ฌ์šฉ
for (const key of m.keys()) {
  console.log(key);
}

// values() ์‚ฌ์šฉ
for (const value of m.values()) {
  console.log(value);
}
  • ํฌ๊ธฐ์˜ ๊ฒฝ์šฐ
    • ๊ฐ์ฒด: Object.keys(obj).length
    • Map: size ์†์„ฑ์œผ๋กœ ์ง์ ‘ ์ ‘๊ทผ ๊ฐ€๋Šฅ

Things to Remember

  • Be aware that any objects your function receives as parameters might have additional keys.
    • ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ฐ์ฒด์— ์ถ”๊ฐ€์ ์ธ ํ‚ค๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์•Œ์•„๋‘๊ธฐ
  • Use Object.entries to iterate over the keys and values of any object.
    • ๊ฐ์ฒด๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ํ‚ค์™€ ๊ฐ’์„ ์–ป๋Š” ๋ฐฉ๋ฒ•: Object.entries
  • Use a for-in loop with an explicit type assertion to iterate objects when you know exactly what the keys will be.
    • ๊ฐ์ฒด๋ฅผ ์ˆœํšŒํ•  ๋•Œ, ํ‚ค๊ฐ€ ์–ด๋–ค ํƒ€์ž…์ธ์ง€ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด for-in ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. (let k: keyof T)
  • Consider Map as an alternative to objects since it's easier to iterate over.
    • ๋งต์ด ์ˆœํšŒํ•˜๊ธฐ ๋” ์‰ฝ๋‹ค. object๋ฅผ ๋Œ€์ฒดํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ