# ๐Ÿ“„ Item39 ~ 43


# Item39. any๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ

  • any ํƒ€์ž…์˜ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์ •๊ทœ์‹์ด๋‚˜ ํ•จ์ˆ˜์— ๋„ฃ๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.
// bad!
function getLengthBad(array: any) {
  return array.length;
}

// good!
function getLength(array: any[]) {
  return array.length;
}
  • getLength ํ•จ์ˆ˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ์ด์œ 
    • ํ•จ์ˆ˜ ๋‚ด์˜ array.length ํƒ€์ž…์ด ์ฒดํฌ๋จ
    • ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์ด any ๋Œ€์‹  number๋กœ ์ถ”๋ก ๋จ
    • ํ•จ์ˆ˜ ํ˜ธ์ถœ๋  ๋•Œ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ๋ฐฐ์—ด์ธ์ง€ ์ฒดํฌ๋จ
  • ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ตฌ์ฒดํ™” ์˜ˆ์‹œ
function hasTwelveLetterKey(o: {[key: string]: any}) {
  for (const key in o) {
    if (key.length === 12) {
      return true;
    }
  }
    
  return false;
}
  • ํ•จ์ˆ˜์˜ ํƒ€์ž…์— any ์ ์šฉํ•˜๋Š” ์˜ˆ์‹œ
type Fn0 = () => any; // ๋งค๊ฐœ๋ณ€์ˆ˜ ์—†์ด ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ํ•จ์ˆ˜
type Fn1 = (arg: any) => any; // ๋งค๊ฐœ๋ณ€์ˆ˜ 1๊ฐœ
type Fn2 = (...args: any[]) => any; // ๋ชจ๋“  ๊ฐœ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ("Function" ํƒ€์ž…๊ณผ ๋™์ผ)
const numArgsBad = (...args: any) => args.length; // any๋ฅผ ๋ฐ˜ํ™˜
const numArgsGood = (...args: any[]) => args.length; // number๋ฅผ ๋ฐ˜ํ™˜
  • any๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ •๋ง๋กœ ๋ชจ๋“  ๊ฐ’์ด ํ—ˆ์šฉ๋˜์–ด์•ผ๋งŒ ํ•˜๋Š”์ง€ ๋ฉด๋ฐ€ํžˆ ๊ฒ€ํ† ํ•ด์•ผ ํ•œ๋‹ค.
  • any ๋ณด๋‹ค ๋” ์ •ํ™•ํ•˜๊ฒŒ ๋ชจ๋ธ๋งํ•  ์ˆ˜ ์žˆ๋„๋ก any[] ๋˜๋Š” {[id: string]: any} ๋˜๋Š” () => any ์ฒ˜๋Ÿผ ๊ตฌ์ฒด์ ์ธ ํ˜•ํƒœ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

# Item40. ํ•จ์ˆ˜ ์•ˆ์œผ๋กœ ํƒ€์ž… ๋‹จ์–ธ๋ฌธ ๊ฐ์ถ”๊ธฐ

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

# (1) cacheLast ํ•จ์ˆ˜ ์˜ˆ์‹œ

declare function cacheLast<T extends Function>(fn: T): T;
declare function shallowEqual(a: any, b: any): boolean;

function cacheLast<T extends Function)(fn: T): T {
  let lastArgs: any[] | null = null;
  let lastResult: any;
    
  return function(...args: any[]) {
    // ~~~~~~~~~~~~~~~~~~~~~
    // '(...args: any[]) => any' ํ˜•์‹์€ 'T' ํ˜•์‹์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
    if (!lastArgs || !shallowEqual(lastArgs, args)) {
      lastResult = fn(...args);
      lastArgs = args;
    }
    return lastResult;
  }
}
  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ๋ฐ˜ํ™˜๋ฌธ์— ์žˆ๋Š” ํ•จ์ˆ˜์™€ ์›๋ณธ ํ•จ์ˆ˜์— ์žˆ๋Š” ํ•จ์ˆ˜ T ํƒ€์ž…์ด ์–ด๋–ค ๊ด€๋ จ์ด ์žˆ๋Š”์ง€ ์•Œ์ง€ ๋ชปํ•˜์—ฌ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ ๊ฒฐ๊ณผ์ ์œผ๋กœ ์›๋ณธ ํ•จ์ˆ˜ T ํƒ€์ž…๊ณผ ๋™์ผํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํ˜ธ์ถœ๋˜๊ณ  ๋ฐ˜ํ™˜๊ฐ’ ์—ญ์‹œ ์˜ˆ์ƒํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ํƒ€์ž… ๋‹จ์–ธ๋ฌธ์„ ์ถ”๊ฐ€ํ•ด์„œ ์˜ค๋ฅ˜๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ํฐ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค.
function cacheLast<T extends Function)(fn: T): T {
  let lastArgs: any[] | null = null;
  let lastResult: any;
    
  return function(...args: any[]) {
    // ~~~~~~~~~~~~~~~~~~~~~
    // '(...args: any[]) => any' ํ˜•์‹์€ 'T' ํ˜•์‹์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
    if (!lastArgs || !shallowEqual(lastArgs, args)) {
      lastResult = fn(...args);
      lastArgs = args;
    }
    return lastResult;
  } as unknown as T;
}

# (2) ๊ฐ์ฒด ๋น„๊ต shallowObjectEqual ํ•จ์ˆ˜ ์˜ˆ์‹œ

declare function shallowObjectEqual<T extends object>(a: T, b: T): boolean;
declare function shallowEqual(a: any, b: any): boolean;

function shallowObjectEqual<T extends object>(a: T, b: T): boolean {
  for (const [k, aVal] of Object.entries(a)) {
    if (!(k in b) || aVal !== (b as any)[k]) {
      return false;
    }
  }
    
  return Object.keys(a).length === Object.keys(b).length;
}
  • k in b ๊ตฌ๋ฌธ์œผ๋กœ ์ฒดํฌํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— b as any ํƒ€์ž… ๋‹จ์–ธ๋ฌธ์€ ์•ˆ์ „ํ•˜๋‹ค.
  • ๊ฐ์ฒด๊ฐ€ ๊ฐ™์€์ง€ ์ฒดํฌํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ์ฒด ์ˆœํšŒ์™€ ๋‹จ์–ธ๋ฌธ์ด ์ฝ”๋“œ์— ์ง์ ‘ ๋“ค์–ด๊ฐ€๋Š” ๊ฒƒ๋ณด๋‹ค, ์•ž์˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋ณ„๋„์˜ ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌํ•ด ๋‚ด๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ์ข‹์€ ์„ค๊ณ„์ด๋‹ค.

# Item41. any์˜ ์ง„ํ™”๋ฅผ ์ดํ•ดํ•˜๊ธฐ

# ๐Ÿ“Œ ํƒ€์ž…์˜ ์ง„ํ™”

  • ๋ฐฐ์—ด์— ๋‹ค์–‘ํ•œ ํƒ€์ž…์˜ ์š”์†Œ๋ฅผ ๋„ฃ์œผ๋ฉด ๋ฐฐ์—ด์˜ ํƒ€์ž…์ด ํ™•์žฅ๋˜๋ฉฐ ์ง„ํ™”ํ•œ๋‹ค.
function range(start: number, limit: number) {
  const out = []; // ํƒ€์ž…์ด any[]
  
  for (let i = start; i < limit; i++) {
    out.push(i); // out์˜ ํƒ€์ž…์ด any[]
  }
    
  return out; // ํƒ€์ž…์ด number[]
}
  • ๋˜ํ•œ ์กฐ๊ฑด๋ฌธ์—์„œ๋Š” ๋ถ„๊ธฐ์— ๋”ฐ๋ผ ํƒ€์ž…์ด ๋ณ€ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
    • ๋งŒ์•ฝ noImplicitAny ์„ค์ •์€ ๋ˆ ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜ ์ถ”๋ก ๋˜๋Š” ํƒ€์ž…์ด ๋ชจ๋‘ any๋กœ ์žกํžˆ๊ฒŒ ๋œ๋‹ค.
let val; // ํƒ€์ž…์ด any

if (Math.random() < 0.5) {
  val = /hello/;
  val; // ํƒ€์ž…์ด RegExp
} else {
  val = 12;
  val; // ํƒ€์ž…์ด number
}

val; // ํƒ€์ž…์ด number | RegExp
  • try/catch ๋ธ”๋ก ์•ˆ์—์„œ ๋ณ€์ˆ˜๋ฅผ ํ• ๋‹นํ•  ๋•Œ ๋ณ€์ˆ˜์˜ ์ดˆ๊นƒ๊ฐ’์ด null์ธ ๊ฒฝ์šฐ๋„ any์˜ ์ง„ํ™”๊ฐ€ ์ผ์–ด๋‚œ๋‹ค.
let val = null; // ํƒ€์ž…์ด any

try {
  somethingDangerous();
  val = 12;
  val; // ํƒ€์ž…์ด number
} catch (e) {
  console.warn('alas!');
}

val; // ํƒ€์ž…์ด number | null

โœ”๏ธ Check Point!

  • ํƒ€์ž…์˜ ์ง„ํ™”๋Š” ๊ฐ’์„ ํ• ๋‹นํ•˜๊ฑฐ๋‚˜ ๋ฐฐ์—ด์— ์š”์†Œ๋ฅผ ๋„ฃ์€ ํ›„์—๋งŒ ์ผ์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์—, ํŽธ์ง‘๊ธฐ์—์„œ๋Š” ์ด์ƒํ•˜๊ฒŒ ๋ณด์ผ ์ˆ˜ ์žˆ๋‹ค.
  • ํ• ๋‹น์ด ์ผ์–ด๋‚œ ์ค„์˜ ํƒ€์ž…์„ ์กฐ์‚ฌํ•ด ๋ด๋„ ์—ฌ์ „ํžˆ any ๋˜๋Š” any[]๋กœ ๋ณด์ผ ๊ฒƒ์ด๋‹ค.

  • ์•”์‹œ์  any ํƒ€์ž…์€ ์–ด๋–ค ๊ฐ’์„ ํ• ๋‹นํ•  ๋•Œ๋งŒ any ํƒ€์ž…์˜ ์ง„ํ™”๊ฐ€ ์ผ์–ด๋‚œ๋‹ค.
    • ๊ทธ๋ฆฌ๊ณ  ์–ด๋–ค ๋ณ€์ˆ˜๊ฐ€ ์•”์‹œ์  any ์ƒํƒœ์ผ ๋•Œ ๊ฐ’์„ ์ฝ์œผ๋ ค๊ณ  ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
    • ๋˜ํ•œ ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ๊ฑฐ์ณ๋„ ์ง„ํ™”ํ•˜์ง€ ์•Š๋Š”๋‹ค.
function makeSquares(start: number, limit: number) {
  const out = []; // 'out' ๋ณ€์ˆ˜๋Š” ์ผ๋ถ€ ์œ„์น˜์—์„œ ์•”์‹œ์ ์œผ๋กœ 'any[]' ํ˜•์‹์ด๋‹ค.
  
  range(start, limit).forEach(i => {
    out.push(i * i);
  });
    
  return out; // 'out' ๋ณ€์ˆ˜์—๋Š” ์•”์‹œ์ ์œผ๋กœ 'any[]' ํ˜•์‹์ด ํฌํ•จ๋œ๋‹ค.
}
  • ์œ„ ์ฝ”๋“œ์™€ ๊ฐ™์ด ๋ฃจํ”„๋กœ ์ˆœํšŒํ•˜๋Š” ๋Œ€์‹ , ๋ฐฐ์—ด์˜ map, filter ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๋‹จ์ผ ๊ตฌ๋ฌธ์œผ๋กœ ๋ฐฐ์—ด์„ ์ƒ์„ฑํ•˜์—ฌ any ์ „์ฒด๋ฅผ ์ง„ํ™”์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • ํƒ€์ž…์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•”์‹œ์  any๋ฅผ ์ง„ํ™”์‹œํ‚ค๋Š” ๋ฐฉ์‹๋ณด๋‹ค ๋ช…์‹œ์  ํƒ€์ž… ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์€ ์„ค๊ณ„์ด๋‹ค.

# Item42. ๋ชจ๋ฅด๋Š” ํƒ€์ž…์˜ ๊ฐ’์—๋Š” any ๋Œ€์‹  unknown์„ ์‚ฌ์šฉํ•˜๊ธฐ

# (1) ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜๊ฐ’๊ณผ ๊ด€๋ จ๋œ unknown

interface Book {
  name: string;
  author: string;
}

function parseYAML(yaml: string): any {
  // ...
}
  • ์œ„ parseYAML ํ•จ์ˆ˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์œผ๋กœ any๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ข‹์ง€ ์•Š๋‹ค.
    • ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ณณ์—์„œ ๋ฐ˜ํ™˜๊ฐ’์„ ์›ํ•˜๋Š” ํƒ€์ž…์œผ๋กœ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์ด ์ด์ƒ์ ์ด๋‹ค.
const book: Book = parseYAML(`
  name: Jane Eyre
  author: Charlotte Bronte
`);
  • ๊ทธ๋Ÿฌ๋‚˜ ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜๊ฐ’์—์„œ ํƒ€์ž… ์„ ์–ธ์„ ๊ฐ•์ œํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, ํ˜ธ์ถœํ•œ ๊ณณ์—์„œ ํƒ€์ž… ์„ ์–ธ์„ ์ƒ๋žตํ•˜๊ฒŒ ๋˜๋ฉด book ๋ณ€์ˆ˜๋Š” ์•”์‹œ์  any ํƒ€์ž…์ด ๋œ๋‹ค.
const book = parseYAML(`
  name: Jane Eyre
  author: Charlotte Bronte
`);

alert(book.title); // ์˜ค๋ฅ˜ ์—†์Œ, ๋Ÿฐํƒ€์ž„์— "undefined" ๊ฒฝ๊ณ 
book('read'); // ์˜ค๋ฅ˜ ์—†์Œ, ๋Ÿฐํƒ€์ž…์— "TypeError: book์€ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค" ์˜ˆ์™ธ ๋ฐœ์ƒ
  • ๋Œ€์‹  parseYAML ์ด unknown ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ๋” ์•ˆ์ „ํ•˜๋‹ค.
function safeParseYAML(yaml: string): unknown {
  return parseYAML(yaml);
}

const book = safeParseYAML(`
  name: Jane Eyre
  author: Charlotte Bronte
`);

alert(book.title); // ~~~ ๊ฐœ์ฒด๊ฐ€ 'unknown' ํ˜•์‹์ž…๋‹ˆ๋‹ค.
book('read'); // ~~~ ๊ฐœ์ฒด๊ฐ€ 'unknown' ํ˜•์‹์ž…๋‹ˆ๋‹ค.
  • ํƒ€์ž… ์ฒด์ปค๋Š” ์ง‘ํ•ฉ ๊ธฐ๋ฐ˜์ด๊ธฐ ๋•Œ๋ฌธ์— any๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํƒ€์ž… ์ฒด์ปค๊ฐ€ ๋ฌด์šฉ์ง€๋ฌผ์ด ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.
  • unknown ํƒ€์ž…์€ ์–ด๋– ํ•œ ํƒ€์ž…์ด๋“  unknown์— ํ• ๋‹น ๊ฐ€๋Šฅํ•˜์ง€๋งŒ unknown ํƒ€์ž…์€ ์˜ค์ง unknown๊ณผ any ์—๋งŒ ํ• ๋‹น ๊ฐ€๋Šฅํ•˜๋‹ค.
    • ๋ฐ˜๋ฉด never ํƒ€์ž…์€ unknown ํƒ€์ž…๊ณผ ์ •๋ฐ˜๋Œ€์ด๋‹ค. ์–ด๋–ค ํƒ€์ž…๋„ never์— ํ• ๋‹นํ•  ์ˆ˜ ์—†์ง€๋งŒ, ์–ด๋– ํ•œ ํƒ€์ž…์œผ๋กœ๋„ ํ• ๋‹น ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํ•œํŽธ unknown ์ƒํƒœ๋กœ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์ ์ ˆํ•œ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋„๋ก ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ๋”ฐ.
    • ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์ธ unknown ๊ทธ๋Œ€๋กœ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ํƒ€์ž… ๋‹จ์–ธ์„ ํ•ด์•ผ ํ•œ๋‹ค.
const book = safeParseYAML(`
  name: Jane Eyre
  author: Charlotte Bronte
`) as Book;

# (2) ๋ณ€์ˆ˜ ์„ ์–ธ๊ณผ ๊ด€๋ จ๋œ unknown

  • ์–ด๋– ํ•œ ๊ฐ’์ด ์žˆ์ง€๋งŒ ๊ทธ ํƒ€์ž…์„ ๋ชจ๋ฅด๋Š” ๊ฒฝ์šฐ์— unknown์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ํƒ€์ž… ๋‹จ์–ธ๋ฌธ์ด unknown์—์„œ ์›ํ•˜๋Š” ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ ์•„๋‹ˆ๋‹ค.
    • instanceof๋ฅผ ์ฒดํฌํ•œ ํ›„ unknown์—์„œ ์›ํ•˜๋Š” ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
function processValue(val: unknown) {
  if (val instanceof Date) {
    val; // ํƒ€์ž…์ด Date
  }
}
  • ๋˜ํ•œ ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œ๋„ unknown์—์„œ ์›ํ•˜๋Š” ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค.
function isBook(val: unknown): val is Book {
  return (
    typeof(val) === 'object' && val !== null && 'name' in val && 'author' in val
  );
}

function processValue(val: unknown) {
  if (isBook(val)) {
    val; // ํƒ€์ž…์ด Book
  }
}
  • ๋˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ œ๋„ค๋ฆญ์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ์ข‹์€ ์Šคํƒ€์ผ์ด ์•„๋‹ˆ๋‹ค.
    • ํƒ€์ž… ๋‹จ์–ธ๋ฌธ๊ณผ ๋‹ฌ๋ผ ๋ณด์ด์ง€๋งŒ ๊ธฐ๋Šฅ์ ์œผ๋กœ๋Š” ๋™์ผํ•˜๋‹ค.
    • ์ œ๋„ค๋ฆญ๋ณด๋‹ค๋Š” unknown์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ๋‹จ์–ธ๋ฌธ์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ์›ํ•˜๋Š” ๋Œ€๋กœ ํƒ€์ž…์„ ์ขํžˆ๋„๋ก ๊ฐ•์ œํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
function safeParseYAML<T>(yaml: string): T {
  return parseYAML(yaml);
}

# (3) ๋‹จ์–ธ๋ฌธ๊ณผ ๊ด€๋ จ๋œ unknown

declare const foo: Foo;
let barAny = foo as any as Bar; // Bad!
let barUnk = foo as unknown as Bar; // Recommend!
  • barAny, barUnk ๋ชจ๋‘ ๊ธฐ๋Šฅ์ ์œผ๋กœ ๋™์ผํ•˜์ง€๋งŒ, unknown ํ˜•ํƒœ๊ฐ€ ๋” ์•ˆ์ „ํ•˜๋‹ค.
    • any์˜ ๊ฒฝ์šฐ๋Š” ๋ถ„๋ฆฌ๋˜๋Š” ์ˆœ๊ฐ„ ๊ทธ ์˜ํ–ฅ๋ ฅ์ด ์ „์—ผ๋ณ‘์œผ๋กœ ํผ์ง€์ง€๋งŒ unknown์˜ ๊ฒฝ์šฐ๋Š” ๋ถ„๋ฆฌ๋˜๋Š” ์ฆ‰์‹œ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ๋” ์•ˆ์ „ํ•˜๋‹ค.

# (4) ์กฐ๊ธˆ ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ unknown

  • object ๋˜๋Š” {}๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ• ์—ญ์‹œ unknown ๋งŒํผ ๋ฒ”์œ„๊ฐ€ ๋„“์€ ํƒ€์ž…์ด์ง€๋งŒ, unknown ๋ณด๋‹ค๋Š” ๋ฒ”์œ„๊ฐ€ ์•ฝ๊ฐ„ ์ข๋‹ค.
    • {} ํƒ€์ž…์€ null๊ณผ undefined๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ๊ฐ’์„ ํฌํ•จํ•œ๋‹ค.
    • object ํƒ€์ž…์€ ๋ชจ๋“  ๋น„๊ธฐ๋ณธํ˜•(non-primitive) ํƒ€์ž…์œผ๋กœ ์ด๋ฃจ์–ด์ง„๋‹ค. ์—ฌ๊ธฐ์—๋Š” true ๋˜๋Š” 12 ๋˜๋Š” "foo" ๊ฐ€ ํฌํ•จ๋˜์ง€ ์•Š์ง€๋งŒ ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์€ ํฌํ•จ๋œ๋‹ค.
  • unknown ํƒ€์ž…์ด ๋„์ž…๋˜๊ธฐ ์ „์—๋Š” {}๊ฐ€ ๋” ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜์—ˆ์ง€๋งŒ, ์ตœ๊ทผ์—๋Š” {}๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๊ฝค ๋“œ๋ฌผ๋‹ค.
    • ์ •๋ง๋กœ null, undefined๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํŒ๋‹จ๋˜๋Š” ๊ฒฝ์šฐ๋งŒ unknown ๋Œ€์‹  {}๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

# Item43. ๋ชฝํ‚ค ํŒจ์น˜๋ณด๋‹ค๋Š” ์•ˆ์ „ํ•œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ธฐ

  • ๊ฐ์ฒด์— ์ž„์˜์˜ ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ข‹์€ ์„ค๊ณ„๊ฐ€ ์•„๋‹ˆ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด window ๋˜๋Š” DOM ๋…ธ๋“œ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.
    • ๊ทธ๋Ÿฌ๋ฉด ๊ทธ ๋ฐ์ดํ„ฐ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ „์—ญ ๋ณ€์ˆ˜๊ฐ€ ๋œ๋‹ค.
    • ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์€์—ฐ์ค‘์— ํ”„๋กœ๊ทธ๋žจ ๋‚ด์—์„œ ์„œ๋กœ ๋ฉ€๋ฆฌ ๋–จ์–ด์ง„ ๋ถ€๋ถ„๋“ค ๊ฐ„์— ์˜์กด์„ฑ์„ ๋งŒ๋“ค๊ฒŒ ๋œ๋‹ค.
    • ๊ทธ๋Ÿฌ๋ฉด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๋ฅผ ๊ณ ๋ คํ•ด์•ผ๋งŒ ํ•œ๋‹ค.
  • ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด๋‹ค.
document.monkey = 'Tamarin'; // ~~~ 'Document' ์œ ํ˜•์— 'monkey' ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
  • ์œ„์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด document์— any ํƒ€์ž…์„ ๋‹จ์–ธํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.
    • ํ•˜์ง€๋งŒ ํƒ€์ž… ์ฒด์ปค๋Š” ํ†ต๊ณผํ•˜์ง€๋งŒ ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ์ƒ์‹คํ•˜๊ณ , ์–ธ์–ด ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค.
  • ์ตœ์„ ์˜ ํ•ด๊ฒฐ์ฑ…์€ document ๋˜๋Š” DOM์œผ๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
    • ๋งŒ์ผ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์—†๋‹ค๋ฉด ๋‘ ๊ฐ€์ง€ ์ฐจ์„ ์ฑ…์ด ์กด์žฌํ•œ๋‹ค.

# ๐Ÿ” interface์˜ ๋ณด๊ฐ•(augmentation) ์‚ฌ์šฉ

interface Document {
  /** ๋ชฝํ‚ค ํŒจ์น˜์˜ ์†(genus) ๋˜๋Š” ์ข…(species) */
  monkey: string;
}

document.monkey = 'Tamarin'; // ์ •์ƒ
  • any ๋ณด๋‹ค ๋ณด๊ฐ•์„ ์‚ฌ์šฉํ•œ ๋ฐฉ๋ฒ•์ด ๋” ์ข‹์€ ์ 
    • ํƒ€์ž…์ด ๋” ์•ˆ์ „ํ•˜๋‹ค. ํƒ€์ž… ์ฒด์ปค๋Š” ์˜คํƒ€๋‚˜ ์ž˜๋ชป๋œ ํƒ€์ž…์˜ ํ• ๋‹น์„ ์˜ค๋ฅ˜๋กœ ํ‘œ์‹œํ•œ๋‹ค.
    • ์†์„ฑ์— ์ฃผ์„์„ ๋ถ™์ผ ์ˆ˜ ์žˆ๋‹ค.
    • ์†์„ฑ์— ์ž๋™์™„์„ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋ชฝํ‚ค ํŒจ์น˜๊ฐ€ ์–ด๋Š ๋ถ€๋ถ„์— ์ ์šฉ๋˜์—ˆ๋Š”์ง€ ์ •ํ™•ํ•œ ๊ธฐ๋ก์ด ๋‚จ๋Š”๋‹ค.
  • ๊ทธ๋Ÿฌ๋‚˜ ๋ชจ๋“ˆ ๊ด€์ ์—์„œ(ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์ด import/export๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ), ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด global ์„ ์–ธ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.
export {};

declare global {
  interface Document {
    /** ๋ชฝํ‚ค ํŒจ์น˜์˜ ์†(genus) ๋˜๋Š” ์ข…(species) */
    monkey: string;
  }
}

document.monkey = 'Tamarin'; // ์ •์ƒ
  • ์ฃผ์˜ ์‚ฌํ•ญ
    • ๋ณด๊ฐ•์€ ์ „์—ญ์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ ์ฝ”๋“œ์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์ด๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์—†๋‹ค.
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋Š” ๋™์•ˆ ์†์„ฑ์„ ํ• ๋‹นํ•˜๋ฉด ์‹คํ–‰ ์‹œ์ ์—์„œ ๋ณด๊ฐ•์„ ์ ์šฉํ•  ๋ฐฉ๋ฒ•์ด ์—†๋‹ค.
      • ex) ์›น ํŽ˜์ด์ง€ ๋‚ด์˜ HTML Element ์กฐ์ž‘์‹œ ์†์„ฑ์„ string | undefined์™€ ๊ฐ™์ด ์„ ์–ธํ•  ์ˆ˜ ์žˆ์–ด ๋” ์ •ํ™•ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹ค๋ฃจ๊ธฐ์—๋Š” ๋” ๋ถˆํŽธํ•ด์ง„๋‹ค.

# ๐Ÿ” ๋” ๊ตฌ์ฒด์ ์ธ ํƒ€์ž… ๋‹จ์–ธ๋ฌธ ์‚ฌ์šฉ

interface MonkeyDocument extends Document {
  /** ๋ชฝํ‚ค ํŒจ์น˜์˜ ์†(genus) ๋˜๋Š” ์ข…(species) */
  monkey: string;
}

(document as MonkeyDocument).monkey = 'Tamarin'; // ์ •์ƒ
  • MonkeyDocument ๋Š” Document๋ฅผ ํ™•์žฅํ•˜๋ฏ€๋กœ ํƒ€์ž… ๋‹จ์–ธ๋ฌธ์€ ์ •์ƒ์ด๋ฉฐ ํ• ๋‹น๋ฌธ์˜ ํƒ€์ž…์€ ์•ˆ์ „ํ•˜๋‹ค.
    • ๋˜ํ•œ Document ํƒ€์ž…์„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ณ„๋„๋กœ ํ™•์žฅํ•˜๋Š” ์ƒˆ๋กœ์šด ํƒ€์ž…์„ ๋„์ž…ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“ˆ ์˜์—ญ ๋ฌธ์ œ๋„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.(import ํ•˜๋Š” ๊ณณ์˜ ์˜์—ญ์—๋งŒ ํ•ด๋‹น)
  • ๋”ฐ๋ผ์„œ ๋ชฝํ‚ค ํŒจ์น˜๋œ ์†์„ฑ์„ ์ฐธ์กฐํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋‹จ์–ธ๋ฌธ์„ ์‚ฌ์š”ํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๋ณ€์ˆ˜๋ฅผ ๋„์ž…ํ•˜๋ฉด ๋œ๋‹ค.
    • ๊ทธ๋Ÿฌ๋‚˜ ๋ชฝํ‚ค ํŒจ์น˜๋ฅผ ๋‚จ์šฉํ•ด์„œ๋Š” ์•ˆ ๋˜๋ฉฐ ๊ถ๊ทน์ ์œผ๋กœ ๋” ์ž˜ ์„ค๊ณ„๋œ ๊ตฌ์กฐ๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.