2024-06-06.md

๐Ÿก

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

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


์•„์ดํ…œ 42: ๋ชจ๋ฅด๋Š” ํƒ€์ž…์˜ ๊ฐ’์—๋Š” any ๋Œ€์‹  unknown์„ ์‚ฌ์šฉํ•˜๊ธฐ Use unknown Instead of any for Values with an Unknown Type

unknown

  • ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜๊ฐ’๊ณผ ๊ด€๋ จ๋œ ํ˜•ํƒœ
  • ๋ณ€์ˆ˜ ์„ ์–ธ๊ณผ ๊ด€๋ จ๋œ ํ˜•ํƒœ
  • ๋‹จ์–ธ๋ฌธ๊ณผ ๊ด€๋ จ๋œ ํ˜•ํƒœ

์˜ˆ์‹œ: parseYAML ํ•จ์ˆ˜

function parseYAML(yaml: string): any {}

interface Book {
  name: string;
  author: string;
}
const book: Book = parseYAML(` // ๋ฐ˜ํ™˜๊ฐ’์„ ์›ํ•˜๋Š” ํƒ€์ž…์œผ๋กœ ํ• ๋‹นํ•ด์„œ ์‚ฌ์šฉ 
  name: Wuthering Heights
  author: Emily Brontรซ
`); // any => Book

// ํ˜ธ์ถœํ•œ ๊ณณ์—์„œ ํƒ€์ž… ์„ ์–ธ์„ ์ƒ๋žตํ•˜๊ฒŒ ๋˜๋ฉด? book ๋ณ€์ˆ˜๋Š” ์•”์‹œ์ ์ธ any ํƒ€์ž…์ด ๋œ๋‹ค.
// ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜ ๋ฐœ์ƒ!!
const book = parseYAML(`
  name: Jane Eyre
  author: Charlotte Brontรซ
`); // any
console.log(book.title); // No error, logs "undefined" at runtime
book("read"); // No error, throws "book is not a function" at runtime

// unknown์„ returnํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜๋‹ค๐Ÿ‘
// unknown ๊ทธ๋Œ€๋กœ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์—, Book์œผ๋กœ ํƒ€์ž… ๋‹จ์–ธ์„ ํ•ด์•ผ ํ•œ๋‹ค.
// ๋‹จ์–ธ? ์• ์ดˆ์— Book ํƒ€์ž…์„ ๊ธฐ๋Œ€ํ•˜๊ณ  ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•จ
function safeParseYAML(yaml: string): unknown {
  return parseYAML(yaml);
}
const book = safeParseYAML(`
  name: The Tenant of Wildfell Hall
  author: Anne Brontรซ
`);
console.log(book.title);
//          ~~~~ 'book' is of type 'unknown'
book("read");
// Error: 'book' is of type 'unknown'
  • ๋ถ€๋ถ„ ์ง‘ํ•ฉ์ด๋ฉด์„œ ์ƒ์œ„ ์ง‘ํ•ฉ์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— any๋Š” ํƒ€์ž… ์‹œ์Šคํ…œ๊ณผ ์ƒ์ถฉ๋˜๋Š” ๋ฉด์ด ์žˆ๋‹ค.
    • ํƒ€์ž… ์ฒด์ปค๋Š” ์ง‘ํ•ฉ ๊ธฐ๋ฐ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฌด์šฉ์ง€๋ฌผ์ด ๋œ๋‹ค.

any & unknown & never

| ํŠน์ง• / ํƒ€์ž… | ์–ด๋–ค ํƒ€์ž…์ด๋“  ํ• ๋‹น ๊ฐ€๋Šฅ | ์–ด๋– ํ•œ ํƒ€์ž…์œผ๋กœ๋„ ํ• ๋‹น๋จ | | ----------- | ----------------------- | ----------------------------------- | | any | O | O (์˜ˆ์™ธ: never์—๋งŒ ํ• ๋‹น ๋ถˆ๊ฐ€) | | unknown | O | X (unknown๊ณผ any์—๋งŒ ํ• ๋‹น ๊ฐ€๋Šฅ) | | never | X | O |

let valueAny: any;
let valueUnknown: unknown;
let valueNever: never;

// ์–ด๋–ค ํƒ€์ž…์ด๋“  ํ• ๋‹น ๊ฐ€๋Šฅ
// โญ•๏ธ any
valueAny = 123; // O
valueAny = "string"; // O
valueAny = true; // O

// โญ•๏ธ unknown
valueUnknown = 123; // O
valueUnknown = "string"; // O
valueUnknown = true; // O

// โŒ never
valueNever = 123; // X
valueNever = "string"; // X
valueNever = true; // X

// ์–ด๋– ํ•œ ํƒ€์ž…์—๋„ ํ• ๋‹น๋จ
let num: number;

num = valueAny; // O (์˜ˆ์™ธ: `never`์—๋งŒ ํ• ๋‹น ๋ถˆ๊ฐ€) // num์˜ ํƒ€์ž…์€ number
num = valueUnknown; // X (number์— unknown์„ ํ• ๋‹นํ•  ์ˆ˜ ์—†๋‹ค.) // num์˜ ํƒ€์ž…์€ number
num = valueNever; // O (์˜ˆ์™ธ ์—†์Œ) // num์˜ ํƒ€์ž…์€ number

image

  • ์–ด๋–ค ํƒ€์ž…์—๋„ never๊ฐ€ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ์‹ ๊ธฐํ•˜๋‹ค. (์ง‘ํ•ฉ ๊ด€์ )

์˜ˆ์‹œ: ๋ณ€์ˆ˜ ์„ ์–ธ

  • ์–ด๋– ํ•œ ๊ฐ’์˜ ํƒ€์ž…์„ ๋ชจ๋ฅด๋Š” ๊ฒฝ์šฐ, unknown์„ ์‚ฌ์šฉํ•œ๋‹ค.
// ๋ญ๋“  ๋“ค์–ด๊ฐ. grab-bag
interface Feature {
  id?: string | number;
  geometry: Geometry;
  properties: unknown;
}

// ๋ฐฐ์—ด ์š”์†Œ์˜ ํƒ€์ž…์„ ๋ชจ๋ฅด๋Š” ๊ฒฝ์šฐ
function isSmallArray(arr: readonly unknown[]): boolean {
  return arr.length < 10;
}

// ์–ด๋–ค ๊ฐ’์ด ๋“ค์–ด์˜ฌ ์ง€ ๋ชจ๋ฅด๋Š” ๊ฒฝ์šฐ
function processValue(value: unknown) {
  if (value instanceof Date) {
    value;
    // ^? (parameter) value: Date
  }
}

// ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œ: unknown ์ขํžˆ๊ธฐ์šฉ
function isBook(value: unknown): value is Book {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    "author" in value
  );
}
function processValue(value: unknown) {
  if (isBook(value)) {
    // ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž… ๊ฐ€๋“œ!
    value;
    // ^? (parameter) value: Book
  }
}

// ์ œ๋„ˆ๋ฆญ์„ ์ž˜๋ชป ์‚ฌ์šฉํ•˜์ง€ ๋ง๊ณ , unknown์„ ๋ฐ˜ํ™˜ํ•˜์ž.
// ์•„๋ž˜ ์ฝ”๋“œ์˜ ์ œ๋„ˆ๋ฆญ์€ ํƒ€์ž… ๋‹จ์–ธ์ด๋‚˜ ๋งˆ์ฐฌ๊ฐ€์ง€์˜ ๊ธฐ๋Šฅ์ด๋‹ค.
function safeParseYAML<T>(yaml: string): T {
  return parseYAML(yaml);
}

ํƒ€์ž… ์บ์ŠคํŒ… / ๋‹จ์–ธ๋ฌธ ์‚ฌ์šฉ ์‹œ

  • ๊ธฐ๋Šฅ์ ์œผ๋กœ ๋™์ผํ•˜๋‹ค.
  • ๋‹ค๋งŒ ๋‚˜์ค‘์— ๋‹จ์–ธ๋ฌธ์„ ๋ถ„๋ฆฌํ•˜๋Š” ๋ฆฌํŒฉํ„ฐ๋ง ๋“ฑ์„ ๊ณ ๋ คํ•˜์—ฌ unknown ์‚ฌ์šฉํ•˜๊ธฐ
declare const foo: Foo;
let barAny = foo as any as Bar; // โŒ
let barUnk = foo as unknown as Bar; // โญ•๏ธ

unknown๊ณผ ์œ ์‚ฌํ•œ ํƒ€์ž…: object ๋˜๋Š” {}

  • ๋ฒ”์œ„๊ฐ€ ๋„“์ง€๋งŒ, unknown๋ณด๋‹ค๋Š” ์ข๋‹ค.
    • {}๋Š” null๊ณผ undefined๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  ๊ฐ’์„ ํฌํ•จํ•œ๋‹ค. (๋นˆ ๊ฐ์ฒด๊ฐ€ ์•„๋‹˜. ํƒ€์ž…์ž„)
    • object๋Š” ๋ชจ๋“  ๋น„๊ธฐ๋ณธํ˜•(non-primitive) ํƒ€์ž…
      • ๊ฐ์ฒด, ๋ฐฐ์—ด ํฌํ•จ
type ABC = {};
const abc: ABC = "abc";

type DEF = object;
const def: DEF = ["def"];
  • unknown ํƒ€์ž… ๋„์ž… ์ „์—๋Š” {}๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ์“ฐ์˜€๋‹ค. ์ตœ๊ทผ์—๋Š” unknown์„ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ์ •๋ง null๊ณผ undefined๊ฐ€ ์•„๋‹ ๋•Œ๋งŒ {}๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ

Things to Remember

  • The unknown type is a type-safe alternative to any. Use it when you know you have a value but do not know or do not care what its type is.
    • unknown์€ any ๋Œ€์‹  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”, ์•ˆ์ „ํ•œ ํƒ€์ž…์ด๋‹ค. ์–ด๋–ค ๊ฐ’์˜ ํƒ€์ž…์„ ์•Œ์ง€๋ชปํ•˜๋ฉด unknown์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • Use unknown to force your users to use a type assertion or other form of narrowing.
    • unknown์„ ์‚ฌ์šฉํ•ด์„œ ์‚ฌ์šฉ์ž๊ฐ€ ํƒ€์ž… ๋‹จ์–ธ๋ฌธ์ด๋‚˜ ํƒ€์ž… ์ฒดํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Avoid return-only type parameters, which can create a false sense of security.
    • ํƒ€์ž… ๋‹จ์–ธ์ด๋‚˜ ๋งˆ์ฐฌ๊ฐ€์ง€์ธ ์ œ๋„ˆ๋ฆญ ์‚ฌ์šฉ ์กฐ์‹ฌ (์•ˆ์ „ํ•œ ํƒ€์ž…์ด๋ผ๊ณ  ์ฐฉ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.)
  • Understand the difference between {}, object, and unknown.
    • {}, object, unknown ํƒ€์ž…์˜ ์ฐจ์ด์  ์•Œ๊ธฐ