[Effective Typescript] any νƒ€μž…

강은비·2022λ…„ 8μ›” 4일
1

TS

λͺ©λ‘ 보기
10/10
post-thumbnail

πŸ‘€ any νƒ€μž…

  • 단어 의미 κ·ΈλŒ€λ‘œ λͺ¨λ“  νƒ€μž…μ„ ν—ˆμš©ν•  수 μžˆλŠ” νƒ€μž…μ΄λ‹€.
  • 기쑴에 JS둜 κ΅¬ν˜„λ˜μ–΄ μžˆλŠ” μ½”λ“œμ— TSλ₯Ό μ μ§„μ μœΌλ‘œ μ μš©ν•  λ•Œ ν™œμš©ν•˜λ©΄ μ’‹λ‹€. πŸ‘
  • ν•˜μ§€λ§Œ λ¬΄λΆ„λ³„ν•˜κ²Œ any νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ TS νƒ€μž… μ‹œμŠ€ν…œμ˜ μž₯점을 λˆ„λ¦΄ 수 μ—†κ²Œ λœλ‹€. 😭

νƒ€μž… μ•ˆμ •μ„±μ΄ μ—†λ‹€.

let age: number;
age = '12' as any;
age += 1; 

// λŸ°νƒ€μž„μ— 정상 μž‘λ™, ageλŠ” "121"

μœ„ μ½”λ“œλ₯Ό 보면 ageλŠ” number νƒ€μž…μœΌλ‘œ μ„ μ–Έλ˜μ—ˆμ§€λ§Œ as any (νƒ€μž…λ‹¨μ–Έλ¬Έ)λ₯Ό μ‚¬μš©ν•˜μ—¬ string νƒ€μž…μ˜ 값을 age λ³€μˆ˜μ— ν• λ‹Ήν–ˆλ‹€.

πŸ‘‰ any νƒ€μž…μ„ μ΄μš©ν•˜λ©΄ νƒ€μž… 체컀λ₯Ό 무λ ₯ν™”μ‹œμΌœ νƒ€μž… μ•ˆμ •μ„±μ„ 보μž₯ν•  수 μ—†κ²Œ λœλ‹€.>

μžλ™μ™„μ„± κΈ°λŠ₯ X

  • λ³€μˆ˜μ— λͺ…ν™•ν•œ νƒ€μž…μ„ λΆ€μ—¬ν•˜λ©΄ VSCodeμ—μ„œ ν•΄λ‹Ή νƒ€μž…μ— λŒ€ν•œ APIλ₯Ό 미리 보기둜 λ„μ›Œμ€„ 수 있고 tab으둜 λΉ λ₯΄κ³  μ •ν™•ν•˜κ²Œ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆλ‹€.
  • ν•˜μ§€λ§Œ any νƒ€μž…μ„ λΆ€μ—¬ν•˜λ©΄ μžλ™μ™„μ„± κΈ°λŠ₯의 도움을 받지 λͺ»ν•œλ‹€.
  • λͺ…μ‹œλœ νƒ€μž…μ„ μ‚¬μš©ν–ˆμ„ λ•Œλ³΄λ‹€ 생산성 μΈ‘λ©΄μ—μ„œ λΆˆλ¦¬ν•˜λ‹€.

🧩 any νƒ€μž… μ‚¬μš©ν•˜κΈ°

any νƒ€μž…μ„ ꡬ체적으둜 λ³€ν˜•ν•΄μ„œ μ‚¬μš©ν•˜μž

일반적인 μƒν™©μ—μ„œλŠ” any보닀 더 ꡬ체적으둜 ν‘œν˜„ν•  수 μžˆλŠ” νƒ€μž…μ΄ μ‘΄μž¬ν•  κ°€λŠ₯성이 λ†’κΈ° λ•Œλ¬Έμ— 더 ꡬ체적인 νƒ€μž…μ„ μ°Ύμ•„ νƒ€μž… μ•ˆμ •μ„±μ„ 높이도둝 ν•΄μ•Ό ν•œλ‹€.

Example

λ°°μ—΄

// **BAD**
function getLength(arr: any) {
  return array.length; // any
}

// **GOOD**
function getLength(arr: any[]) {
  return array.length; // number
}
  • ν•¨μˆ˜ λ‚΄μ˜ array.length νƒ€μž…μ΄ μ²΄ν¬λœλ‹€.
  • ν•¨μˆ˜μ˜ λ°˜ν™˜ νƒ€μž…μ΄ any λŒ€μ‹  number둜 μΆ”λ‘ λœλ‹€.
  • ν•¨μˆ˜ 호좜될 λ•Œ λ§€κ°œλ³€μˆ˜κ°€ 배열인지 μ²΄ν¬λœλ‹€.

객체

  • ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜κ°€ 객체인지 ν•˜μ§€λ§Œ, 속성을 μ•Œ 수 μ—†λ‹€λ©΄ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ„ λ‹€μŒκ³Ό 같이 μ„ μ–Έν•˜λ©΄ λœλ‹€.
function hasTweleveLetterKey(o: {[key: string]: any}) {
  for (const key in o) {
    if (key.length == 12) {
      console.log(key, o[key]);
      return true;
    }
  }
  return false;
}
  • {[key:string]: any} λŒ€μ‹  λͺ¨λ“  λΉ„κΈ°λ³Έν˜• νƒ€μž…μ„ ν¬ν•¨ν•˜λŠ” object νƒ€μž…μ„ μ‚¬μš”μ•Œ 수 μžˆλ‹€. object νƒ€μž…μ€ 객체의 ν‚€λ₯Ό μ—΄κ±°ν•  μˆ˜λŠ” μžˆμ§€λ§Œ, 속성에 μ ‘κ·Όν•  수 μ—†λ‹€.
function hasTweleveLetterKey(o: object) {
  for (const key in o) {
    if (key.length == 12) {
      console.log(key, o[key]); // ERROR
      return true;
    }
  }
  return false;
}

ν•¨μˆ˜

  • ν•©μˆ˜ νƒ€μž…μ— λŒ€ν•΄μ„œλ„ any λŒ€μ‹  μ΅œμ†Œν•œμœΌλ‘œλ§ˆλ‚˜ νƒ€μž…μ„ ꡬ체화해야 ν•œλ‹€.
type Fn0 = () => any; // λ§€κ²¨λ³€μˆ˜ 없이 호좜 κ°€λŠ₯ν•œ λͺ¨λ“  ν•¨μˆ˜
type Fn1 = (arg: any) => any; // λ§€κ°œλ³€μˆ˜ 1개
type Fn2 = (...args: any[]) => any; // λͺ¨λ“  개수의 λ§€κ°œλ³€μˆ˜ - "Function" νƒ€μž…κ³Ό λ™μΌν•˜λ‹€.

πŸ‘‰ anyλ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” μ •λ§λ‘œ λͺ¨λ“  값이 ν—ˆμš©λ˜μ–΄μ•Όλ§Œ ν•˜λŠ”μ§€ λ©΄λ°€νžˆ κ²€ν† ν•΄μ•Ό ν•œλ‹€.


πŸš€ any의 진화

μ•”μ‹œμ  any νƒ€μž…μ— μ–΄λ–€ 값을 ν• λ‹Ήν•  λ•Œ any νƒ€μž…μ€ ꡬ체적인 νƒ€μž…μœΌλ‘œ μ§„ν™”ν•œλ‹€. anyκ°€ 진화방식은 일반적인 λ³€μˆ˜κ°€ μΆ”λ‘ λ˜λŠ” 원리와 λ™μΌν•˜λ‹€.

let val; // any
if (Math.random() < 0.5) {
  val = '/hello/'; // RegExp
}
else {
  val = 12; // number
}
// val: number | RegExp

배열에 λ‹€μ–‘ν•œ νƒ€μž…μ˜ μš”μ†Œλ₯Ό λ„£μœΌλ©΄ λ°°μ—΄μ˜ νƒ€μž…μ΄ ν™•μž₯되며 μ§„ν™”λœλ‹€.

function range(start: number, limit: number){
  const out = []; // any[]
  for (let i = start; i < limit; i++) {
    out.push(i);
  }
  return out; // number[]
}

any νƒ€μž…μ˜ μ§„ν™”λŠ” noImplicitAnyκ°€ μ„€μ •λœ μƒνƒœμ—μ„œ λ³€μˆ˜μ˜ νƒ€μž…μ΄ μ•”μ‹œμ  any인 κ²½μš°μ—λ§Œ μΌμ–΄λ‚œλ‹€. λͺ…μ‹œμ μœΌλ‘œ anyλ₯Ό μ„ μ–Έν•˜λ©΄ νƒ€μž…μ΄ κ·ΈλŒ€λ‘œ μœ μ§€λœλ‹€.

let val: any; // any
if (Math.random() < 0.5) {
  val = '/hello/'; // any
}
else {
  val = 12; // any
}
// val: any

μ•”μ‹œμ  any νƒ€μž…μ€ ν•¨μˆ˜ ν˜ΈμΆœμ„ 거쳐도 μ§„ν™”ν•˜μ§€ μ•ŠλŠ”λ‹€.

// **BAD**
function makeSquares(start: number, limit: number) {
  const out = []; // any[]
  range(start, limit).forEach(i => {
    out.push(i*i);
  });
  return out; // any[]
}
// **GOOD**
function makeSquares(start: number, limit: number) {
  const out = range(start, limit).map(i => i*i); // number[]
  return out;
}

πŸ‘‰ μ•”μ‹œμ  any νƒ€μž…μ— 값을 ν• λ‹Ήν•˜κ±°λ‚˜ any[] νƒ€μž…μ— μš”μ†Œλ₯Ό λ„£μ–΄ νƒ€μž…μ΄ 진화할 수 μžˆμ§€λ§Œ, λͺ…μ‹œμž‘ νƒ€μž… ꡬ문을 μ‚¬μš©ν•˜λŠ” 것이 νƒ€μž… μ•ˆμ •μ„±μ΄ 더 λ†’λ‹€.


any πŸ†š unknown

곡톡점 : μ–΄λ–€ νƒ€μž…μ΄λ“  any ν˜Ήμ€ unknown νƒ€μž…μ— ν• λ‹Ή κ°€λŠ₯ν•˜λ‹€.

const a: any = 1;
const b: unknown = "string";

차이점

  • any νƒ€μž…μ€ μ–΄λ–€ νƒ€μž…μœΌλ‘œλ„ 할당이 κ°€λŠ₯ν•œλ° unknown νƒ€μž…μ€ 였직 unknownκ³Ό any νƒ€μž…μ—λ§Œ ν• λ‹Ή κ°€λŠ₯ν•˜λ‹€.
const c: number = a;
const d: string = b; // ERROR (Type 'unknown' is not assignable to type 'string'.)
  • any νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•Šμ§€λ§Œ, unknown νƒ€μž…μΈ μ±„λ‘œ 값을 μ‚¬μš©ν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•œλ‹€.
// ν•¨μˆ˜ λ°˜ν™˜κ°’μ΄ any νƒ€μž…
interface Book {
  name: string;
  author: string;
}

function parseYAML(yaml: string): any {
  // ...
}

const book = parseYAML(`
  name: Jane Eytre
  author: Charlotte Bronte
`);
alert(book.title); // 였λ₯˜ μ—†μŒ. λŸ°νƒ€μž„μ— "undefined" μ—λŸ¬ λ°œμƒ
book("read"); // 였λ₯˜ μ—†μŒ. λŸ°νƒ€μž„μ— "TypeError" λ°œμƒ
// ν•¨μˆ˜ λ°˜ν™˜κ°’μ΄ unknown νƒ€μž…
function safeParseYAML(yaml: string): unknown {
  return parseYAML(yaml);
}

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

alert(book.title); // ERROR: κ°œμ²΄κ°€ unknown ν˜•μ‹μž…λ‹ˆλ‹€.
book("read"); // ERROR: κ°œμ²΄κ°€ unknown ν˜•μ‹μž…λ‹ˆλ‹€.
  • unknown νƒ€μž…μ„ κ·ΈλŒ€λ‘œ μ‚¬μš©ν•  수 μ—†κΈ° λ•Œλ¬Έμ— νƒ€μž… λ‹¨μ–Έλ¬Έμ΄λ‚˜ μ‚¬μš©μž μ •μ˜ νƒ€μž… κ°€λ“œ ν˜Ήμ€ instanceof μ—°μ‚°μžλ₯Ό μ΄μš©ν•˜μ—¬ unknown νƒ€μž…μ„ μ›ν•˜λŠ” νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•΄μ•Ό ν•œλ‹€.
// νƒ€μž… 단언
const book = safeParseYAML(`
  name: Jane Eytre
  author: Charlotte Bronte
`) as Book;

alert(book.title); // ERROR: 'Book' ν˜•μ‹μ— 'title' 속성이 μ—†μŠ΅λ‹ˆλ‹€.
book("read"); // ERROR: 이 식은 ν˜ΈμΆœν•  수 μ—†μŠ΅λ‹ˆλ‹€.
// μ‚¬μš©μž μ •μ˜ νƒ€μž… κ°€λ“œ
function isBook(val: unknown): val is Book {
  return (
    typeof(val) === 'object' && val !== null && 'name' in val && 'authon' in val
  );
}

function processValue(val: unknown) {
  if (isBook(val)) {
    // val: Book
  }
}
// instanceof
function processValue(val: unknown) {
  if (val instanceof Date) {
    // val: Date
  }
}

πŸ‘‰ νƒ€μž…μ„ λͺ¨λ₯Ό λ•ŒλŠ” any λŒ€μ‹  unknown을 μ‚¬μš©ν•˜μž!


νƒ€μž… 컀버리지

noImplicitAny λ₯Ό μ„€μ •ν•˜κ³  λͺ¨λ“  μ•”μ‹œμ  any λŒ€μ‹  λͺ…μ‹œμ  νƒ€μž… ꡬ문을 좔가해도 any νƒ€μž…κ³Ό κ΄€λ ¨λœ λ¬Έμ œλ“€λ‘œλΆ€ν„° μ•ˆμ „ν•˜λ‹€κ³  ν•  수 μ—†λ””.

  • λͺ…μ‹œμ  any νƒ€μž…
  • μ„œλ“œνŒŒν‹° νƒ€μž… μ„ μ–Έ (@types)

type-coverage νŒ¨ν‚€μ§€

$ npx type-coverage
9985 / 10117 98.69/5

$ npx type-coverage --detail
path/to/code.ts:1:10 getColumnInfo
path/to/module.ts:7:1 pt2

πŸ‘‰ type-coverage νŒ¨ν‚€μ§€λ₯Ό μ΄μš©ν•˜μš” any νƒ€μž…μ„ 좔적할 수 μžˆλ‹€.

0개의 λŒ“κΈ€