2024-05-30.md

🏑

DIL: μ΄νŽ™ν‹°λΈŒ νƒ€μž…μŠ€ν¬λ¦½νŠΈ

μŠ€ν„°λ””: μ›”κ°„ CS, https://github.com/monthly-cs/2024-05-effective-typescript
μž‘μ„±μΌ: 2024-05-30
μž‘μ„±μž: dusunax


μ•„μ΄ν…œ 33: string νƒ€μž…λ³΄λ‹€ 더 ꡬ체적인 νƒ€μž… μ‚¬μš©ν•˜κΈ° Prefer More Precise Alternatives to String Types

λΆˆλΆ„λͺ…ν•œ string νƒ€μž… μ‚¬μš©

  • λ¬Έμžμ—΄μ„ λ‚¨λ°œν•˜μ—¬ μƒμ„±λ˜μ—ˆλ‹€. stringly typed.
interface Album {
  artist: string;
  title: string;
  releaseDate: string; // YYYY-MM-DD
  recordingType: string; // E.g., "live" or "studio"
  // 잘λͺ»λœ 값을 μž…λ ₯해도, νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ μ•Œ 수 μ—†μ–΄ 였λ₯˜λ‘œ λ“œλŸ¬λ‚˜μ§€ μ•ŠμŒ
}

// κ°œμ„ 
/** λ…ΉμŒμ΄ μ§„ν–‰λœ ν™˜κ²½ */
type RecordingType = "studio" | "live";
// νƒ€μž…μ„ λͺ…μ‹œμ μœΌλ‘œ μ •μ˜
// ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” κ³³μ—μ„œ νƒ€μž…μ˜ μ„€λͺ…을 λ³Ό 수 μžˆλ‹€

interface Album {
  artist: string;
  title: string;
  releaseDate: Date;
  recordingType: RecordingType;
}
  1. λ‹€λ₯Έ 곳으둜 값이 μ „λ‹¬λ˜μ–΄λ„ μ‰½κ²Œ νƒ€μž…μ„ 확인할 수 μžˆλ‹€.
  2. νƒ€μž…μ˜ μ •μ˜ν•˜κ³ , 주석을 뢙일 수 μžˆλ‹€
  3. keyof μ—°μ‚°μžλ‘œ 객체의 속성 체크가 κ°€λŠ₯ν•˜λ‹€.

μ˜ˆμ‹œ: μ œλ„ˆλ¦­ νƒ€μž…μ„ μ‚¬μš©ν•˜λŠ” μ˜ˆμ‹œ

// ❗️ anyλ₯Ό μ‚¬μš©ν•΄ μ •λ°€ν•˜μ§€ λͺ»ν•˜λ‹€.
// λ°˜ν™˜κ°’μ— anyλ₯Ό μ‚¬μš©ν•˜λŠ” 것은 맀우 쒋지 μ•Šλ‹€.
function pluckA(records: any[], key: string): any[] {
  return records.map((r) => r[key]);
}

// ❗️ μ œλ„ˆλ¦­ νƒ€μž…
function pluckB<T>(records: T[], key: string): any[] {
  return records.map((r) => r[key]);
  // keyλŠ” "artist" | "title" | "releaseDate" | "recordingType" 만 μœ νš¨ν•˜λ‹€.
  // μ§€κΈˆμ€ string.
  // κ·Έλ ‡λ‹€λ©΄?πŸ€”
}

// Key의 νƒ€μž…
type K = keyof Album;
// κ·ΈλŸ¬λ―€λ‘œ, μŠ€νŠΈλ§μ„ keyof T둜 λ°”κΎΈλ©΄ λœλ‹€.
// ❗️ keyof T
function pluckC<T>(records: T[], key: keyof T) {
  return records.map((r) => r[key]);
}
// νƒ€μž… 체컀λ₯Ό ν†΅κ³Όν•˜κ³ , λ°˜ν™˜ νƒ€μž…μ„ μΆ”λ‘ ν•˜κ³ , μΆ”λ‘ λœ νƒ€μž…μ„ μ•Œ 수 μžˆλ‹€.
// function pluck<T>(records: T[], key: keyof T): T[keyof T][]
// T[keyof T]λŠ”? 아직 λ„ˆλ¬΄ λ„“λ‹€. (string | Date)[]

// ❗️ 두 번째 μ œλ„ˆλ¦­ μΆ”κ°€ν•˜κΈ° πŸ‘
// K extends keyof T : keyof T의 λΆ€λΆ„ 집합
// keyof T의 λ²”μœ„μ— μ†ν•˜λŠ” 값이닀. 단일 κ°’
function pluckD<T, K extends keyof T>(records: T[], key: K): T[K][] {
  return records.map((r) => r[key]);
}
pluckD<Album, "releaseDate">([someAlbum], "releaseDate");
// Date[]
// [Date: "2024-05-30T00:00:00.000Z"]
  • pluckC: μ œλ„ˆλ¦­ image
  • pluckD: 2가지 μ œλ„ˆλ¦­ image

keyof와 K extends keyof T μ‚΄νŽ΄λ³΄κΈ°

  • νƒ€μž… μ œλ„€λ¦­κ³Ό κ΄€λ ¨λœ ν‚€μ›Œλ“œμ™€ ꡬ문

βœ… keyof ν‚€μ›Œλ“œ

  • keyof TλŠ” νƒ€μž… T의 λͺ¨λ“  ν‚€λ₯Ό λ‚˜νƒ€λ‚΄λŠ” νƒ€μž…
{
  name: string;
  age: number;
}
// keyof TλŠ” "name" | "age"와 같은 μœ λ‹ˆμ–Έ νƒ€μž…

βœ… K extends keyof T

  • Kκ°€ keyof T의 μ„œλΈŒνƒ€μž…μ΄μ–΄μ•Ό 함을 의미
  • 즉, KλŠ” T의 ν‚€λ“€ 쀑 ν•˜λ‚˜, λ˜λŠ” κ·Έ μ΄μƒμ˜ 쑰합일 수 μžˆλ‹€.
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
// KλŠ” T의 ν‚€λ“€ 쀑 ν•˜λ‚˜μ—¬μ•Ό ν•˜λ―€λ‘œ, obj[key]κ°€ μœ νš¨ν•˜λ‹€.

❌ K: keyof T?

  • μ œλ„€λ¦­ νƒ€μž… Kκ°€ keyof T νƒ€μž…κ³Ό μ •ν™•νžˆ κ°™μŒμ„ λͺ…μ‹œν•˜λ € 함
    • KλŠ” λ°˜λ“œμ‹œ T의 ν‚€λ“€ 쀑 ν•˜λ‚˜λ§Œμ„ κ°€μ§ˆ 수 μžˆλ‹€.
  • K: keyof TλŠ” μœ νš¨ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μ œλ„ˆλ¦­ νƒ€μž…μ„ μ œν•œν•  λ•ŒλŠ” 항상 extends ν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. K: keyof TλŠ” λ¬Έλ²•μ μœΌλ‘œ 였λ₯˜!

μ œλ„€λ¦­μ„ μ μ ˆν•˜κ²Œ μ‚¬μš©ν•˜κΈ°

function pluckD<T, K extends keyof T>(records: T[], key: K): T[K][];
  • λ§€κ°œλ³€μˆ˜ νƒ€μž…μ΄ μ •λ°€ν•΄μ‘ŒκΈ° λ•Œλ¬Έμ—, Album의 킀에 μžλ™μ™„μ„± κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.
  • string은 any와 λΉ„μŠ·ν•œ 문제λ₯Ό 가지고 μžˆμŠ΅λ‹ˆλ‹€.
    • 잘λͺ» μ‚¬μš©ν•˜λ©΄?
      • λ¬΄νš¨ν•œ 값을 ν—ˆμš©, νƒ€μž… κ°„μ˜ 관계 감좀
      • μ‹€μ œ 버그λ₯Ό μ°ΎκΈ° λͺ»ν•˜λ„둝 νƒ€μž… 체컀λ₯Ό λ°©ν•΄ν•œλ‹€.
  • μ •ν™•ν•œ νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄?
    • 였λ₯˜λ₯Ό λ°©μ§€ν•˜κ³ , μ½”λ“œμ˜ 가독성을 ν–₯상 μ‹œν‚¨λ‹€.

Things to Remember

  • Avoid "stringly typed" code. Prefer more appropriate types where not every string is a possibility.
    • λ¬Έμžμ—΄μ„ λ‚¨λ°œν•΄ μ„ μ–Έν•œ μ½”λ“œλ₯Ό ν”Όν•˜μž.
    • string은 λͺ¨λ“  λ¬Έμžμ—΄μ„ ν• λ‹Ήν•  수 있기 λ•Œλ¬Έμ—, ꡬ체적인 νƒ€μž…μ„ μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€~
  • Prefer a union of string literal types to string if that more accurately describes the domain of a variable. You'll get stricter type checking and improve the development experience.
    • λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄ νƒ€μž…μ˜ μœ λ‹ˆμ˜¨μ„ μ‚¬μš©ν•˜λŠ” 것이, νƒ€μž… 체크λ₯Ό 더 μ—„κ²©νžˆ ν•  수 있고 생산성을 ν–₯μƒμ‹œμΌœ μ’‹λ‹€(μžλ™ μ™„μ„±, μ˜€νƒ€ 확인)
  • Prefer keyof T to string for function parameters that are expected to be properties of an object.
    • 객체의 속성λͺ…을 => ν•¨μˆ˜ λ§€κ°œλ³€μˆ˜λ‘œ 받을 λ•ŒλŠ”?
      • string보닀 keyof Tλ₯Ό μ‚¬μš©ν•˜μž