2024-05-21.md

🏑

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

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


νƒ€μž…μŠ€ν¬λ¦½νŠΈ

μ•„μ΄ν…œ 19: μΆ”λ‘  κ°€λŠ₯ νƒ€μž…μ„ μ‚¬μš©ν•΄ μž₯ν™©ν•œ μ½”λ“œ λ°©μ§€ν•˜κΈ° Avoid Cluttering Your Code with Inferable Types

νƒ€μž… μΆ”λ‘ 

  • λͺ¨λ“  λ³€μˆ˜μ— νƒ€μž… μ„ μ–Έν•˜λŠ” 것은 비생산적 / λΆˆν•„μš”
// not good!
let x: number = 12;

// enough!
let x = 12;
  • νƒ€μž… 좔둠이 λœλ‹€λ©΄ λͺ…μ‹œμ  νƒ€μž… ꡬ문은 ν•„μš”ν•˜μ§€ μ•ŠμŒ (였히렀 λ°©ν•΄)
  • λ³΅μž‘ν•œ 객체 λ˜λŠ” ν•¨μˆ˜μ˜ λ°˜ν™˜ νƒ€μž…λ„ μΆ”λ‘ ν•  수 있음
// 객체 μΆ”λ‘ 
const person = {
  name: "Sojourner Truth",
  born: {
    where: "Swartekill, NY",
    when: "c.1797",
  },
  died: {
    where: "Battle Creek, MI",
    when: "Nov. 26, 1883",
  },
};
// {
//   name: string;
//   born: {
//     where: string;
//     when: string;
//   };
//   died: {
//     where: string;
//     when: string;
//   }
// }

// ν•¨μˆ˜μ˜ λ°˜ν™˜ νƒ€μž… μΆ”λ‘ 
function square(nums: number[]) {
  return nums.map((x) => x * x);
}
const squares = square([1, 2, 3, 4]);
//    ^? const squares: number[]
  • μœ λ‹› νƒ€μž… μΆ”λ‘  (보닀 μ •ν™•ν•œ νƒ€μž… 좔둠을 톡해 νƒ€μž… 였λ₯˜ 방지!)

    image

  • ꡬ쑰뢄해 ν• λ‹Ή(비ꡬ쑰화 ν• λ‹Ή)을 톡해 지역 λ³€μˆ˜μ˜ νƒ€μž…μ΄ μΆ”λ‘ λ˜λ„λ‘ ν•˜μž. (λͺ…μ‹œμ  νƒ€μž… μ„ μ–ΈX)

function logProduct(product: Product) {
  const id: number = product.id;
  // ~~ Type 'string' is not assignable to type 'number'
  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);
}

λ§€κ°œλ³€μˆ˜ νƒ€μž… λͺ…μ‹œ

  • νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” λ§€κ°œλ³€μˆ˜μ˜ μ΅œμ’… μ‚¬μš©μ²˜κΉŒμ§€ κ³ λ €ν•˜μ§€ μ•ŠλŠ”λ‹€

    • λ³€μˆ˜μ˜ νƒ€μž…μ€ 처음 λ“±μž₯ν•  λ•Œ κ²°μ •λœλ‹€
    • ν•¨μˆ˜/λ©”μ„œλ“œ μ‹œκ·Έλ‹ˆμ²˜μ— νƒ€μž… ꡬ문을 ν¬ν•¨ν•˜κ³ , ν•¨μˆ˜ λ‚΄μ˜ 지역 λ³€μˆ˜μ—λŠ” νƒ€μž… ꡬ문을 넣지 μ•ŠκΈ° (μ½λŠ” μ‚¬λžŒμ΄ κ΅¬ν˜„ λ‘œμ§μ— 집쀑할 수 있음)
  • λ§€κ°œλ³€μˆ˜ 기본값이 μžˆλŠ” 경우, νƒ€μž…μ„ μΆ”λ‘ ν•œλ‹€

    function parseNumber(str: string, base = 10) {
      //                              ^? (parameter) base: number
      // ...
    }
    

νƒ€μž… 정보가 μžˆλŠ” 라이브러리

  • νƒ€μž… μ§€μ›ν•˜λŠ” λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ 콜백 ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ€ μžλ™μœΌλ‘œ μΆ”λ‘ λœλ‹€(보톡)

    // Don't do this:
    app.get("/health", (request: express.Request, response: express.Response) => {
      response.send("OK");
    });
    
    // Do this:
    app.get("/health", (request, response) => {
      //                ^? (parameter) request: Request<...>
      response.send("OK");
      // ^? (parameter) response: Response<...>
    });
    
  • μ˜ˆμ‹œ

    import _ from "lodash";
    
    interface FoodItem {
      icon: string;
      category: string;
      price: number;
    }
    
    const foodItems: FoodItem[] = [
      { icon: "πŸ”", category: "Fast Food", price: 5000 },
      { icon: "πŸ•", category: "Fast Food", price: 8000 },
      { icon: "🍣", category: "Japanese", price: 12000 },
      { icon: "🍜", category: "Japanese", price: 10000 },
      { icon: "🍝", category: "Italian", price: 15000 },
      { icon: "πŸ₯—", category: "Healthy", price: 9000 },
    ];
    
    // Lodash의 groupByλ₯Ό μ‚¬μš©ν•  λ•Œ 콜백 ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ΄ μžλ™μœΌλ‘œ 좔둠됨
    const groupedByCategory = _.groupBy(foodItems, (item) => item.category);
    
    console.log(groupedByCategory);
    

νƒ€μž…μ„ λͺ…μ‹œν•˜κ³  싢은 경우: 객체 λ¦¬ν„°λŸ΄ μ •μ˜

  • μ •μ˜μ— νƒ€μž…μ„ λͺ…μ‹œν•˜λ©΄, excess property check(μΆ”κ°€ 속성 체크, μž‰μ—¬ 속성 체크)κ°€ λ™μž‘ν•œλ‹€
    • 선택적 속성이 μžˆλŠ” νƒ€μž…μ˜ μ˜€νƒ€ λ“±μ˜ 였λ₯˜
    • λ³€μˆ˜κ°€ μ‚¬μš©λ˜λŠ” μˆœκ°„μ΄ μ•„λ‹Œ ν• λ‹Ήν•˜λŠ” μ‹œμ μ— 였λ₯˜λ₯Ό ν‘œμ‹œ
const elmo: Product = {
  name: "Tickle Me Elmo",
  id: "048188 627152",
  price: 28.99,
};

νƒ€μž…μ„ λͺ…μ‹œν•˜κ³  싢은 경우: ν•¨μˆ˜ λ°˜ν™˜κ°’

  • νƒ€μž… 좔둠이 κ°€λŠ₯할지라도, κ΅¬ν˜„μƒμ˜ 였λ₯˜κ°€ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ κ³³κΉŒμ§€ 영ν–₯을 λ―ΈμΉ˜μ§€ μ•ŠκΈ° μœ„ν•΄ νƒ€μž… ꡬ문을 λͺ…μ‹œν•  수 μžˆλ‹€.
    • μ•„λž˜ Promise μ˜ˆμ‹œ μ°Έκ³  (μ‹€μ œλ‘œλŠ” async/await을 효과적으둜 μ‚¬μš©ν•˜κΈ°, μ•„μ΄ν…œ 25)
const cache: { [ticker: string]: number } = {};
function getQuote(ticker: string) {
  if (ticker in cache) {
    return cache[ticker]; // κ΅¬ν˜„ 였λ₯˜: Promiseλ₯Ό λ°˜ν™˜ν•΄μ•Ό ν•œλ‹€!
  }
  return fetch(`https://quotes.example.com/?q=${ticker}`)
    .then((response) => response.json())
    .then((quote) => {
      cache[ticker] = quote;
      return quote as number;
    });
}

// ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ κ³³μ—μ„œ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€
getQuote("MSFT").then(considerBuying);
//               ~~~~ Property 'then' does not exist on type
//                    'number | Promise<number>'

// μ˜λ„λœ λ°˜ν™˜ νƒ€μž…μ„ λͺ…μ‹œν•˜μ—¬ => μ—λŸ¬κ°€ λ°œμƒν•œ μœ„μΉ˜λ₯Ό μ •ν™•νžˆ ν‘œκΈ°ν•˜κΈ°
const cache: { [ticker: string]: number } = {};
function getQuote(ticker: string): Promise<number> {
  if (ticker in cache) {
    return cache[ticker];
    // ~~~ Type 'number' is not assignable to type 'Promise<number>'
  }
  // ...
}
  • μž₯점

    1. 였λ₯˜μ˜ μœ„μΉ˜λ₯Ό μ œλŒ€λ‘œ ν‘œμ‹œν•œλ‹€
    2. ν•¨μˆ˜ μ‹œκ·Έλ‹ˆμ²˜λ₯Ό λ”μš± λͺ…ν™•ν•˜κ²Œ ν•œλ‹€ (μž…/좜λ ₯ νƒ€μž… λͺ…μ‹œ)
    3. λͺ…λͺ…λœ νƒ€μž… μ‚¬μš©ν•˜κΈ°
    interface Vector2D {
      x: number;
      y: number;
    }
    function add(a: Vector2D, b: Vector2D): Vector2D {
      return { x: a.x + b.x, y: a.y + b.y };
    }
    

Things to Remember

  • Avoid writing type annotations when TypeScript can infer the same type.
    • νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ νƒ€μž…μ„ μΆ”λ‘ ν•  수 μžˆλ‹€λ©΄, νƒ€μž… ꡬ문 μž‘μ„±ν•˜μ§€ μ•ŠκΈ°
  • Ideal TypeScript code has type annotations in function/method signatures but not on local variables in their bodies.
    • ν•¨μˆ˜/λ©”μ„œλ“œ μ‹œκ·Έλ‹ˆμ²˜μ— νƒ€μž… ꡬ문을 μž‘μ„±ν•˜κ³ , λ‚΄λΆ€ 지역 λ³€μˆ˜μ—λŠ” μž‘μ„±ν•˜μ§€ 말자.
  • Consider using explicit annotations for object literals to enable excess property checking and ensure errors are reported close to where they occur.
    • 객체 λ¦¬ν„°λŸ΄κ³Ό ν•¨μˆ˜ λ°˜ν™˜μ—λŠ” νƒ€μž… λͺ…μ‹œλ₯Ό κ³ λ €ν•˜μž.
    • μΆ”κ°€/μž‰μ—¬ 속성 체크 & μ—λŸ¬κ°€ λ°œμƒν•œ μœ„μΉ˜μ— ν‘œμ‹œλ˜λ„λ‘ ν•˜κΈ°
  • Don't annotate function return types unless the function has multiple returns, is part of a public API, or you want it to return a named type.
    • 곡개된 API ν•¨μˆ˜μ΄κ±°λ‚˜, λͺ…λͺ…λœ νƒ€μž…μ΄ ν•„μš”ν•œ 경우λ₯Ό μ œμ™Έν•˜κ³  리턴 νƒ€μž…μ€ λͺ…μ‹œν•˜μ§€ μ•Šμ•„λ„ 될 것
      • μ•„λ§ˆλ„ μ»¨λ²€μ…˜μ— λ”°λ₯Ό 것 κ°™λ‹€