2024-06-29.md

🏑

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

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


μ•„μ΄ν…œ 58: λͺ¨λ˜ μžλ°”μŠ€ν¬λ¦½νŠΈ μž‘μ„±ν•˜κΈ° μ΄μ–΄μ„œ

ν”„λ‘œν† νƒ€μž… λŒ€μ‹  클래슀 μ‚¬μš©ν•˜κΈ°

  • ES6 class ν‚€μ›Œλ“œ(클래슀 기반 λͺ¨λΈ) λ„μž… 이전 => ν”„λ‘œν† νƒ€μž… 기반의 객체 λͺ¨λΈμ„ μ‚¬μš©ν–ˆλ‹€.
    • λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ λŒ€μƒ μ½”λ“œμ— ν”„λ‘œν† νƒ€μž…μ΄ μžˆλ‹€λ©΄ 클래슀둜 λ°”κΎΈλŠ” 것이 μ’‹λ‹€. 직관적이고 간결함.
  • VS Codeμ—λŠ” TypeScriptλ₯Ό μœ„ν•œ μœ μš©ν•œ λ¦¬νŒ©ν† λ§ κΈ°λŠ₯이 ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, ν•¨μˆ˜ μΆ”μΆœ 및 μƒμˆ˜ μΆ”μΆœ κΈ°λŠ₯이 μžˆμŠ΅λ‹ˆλ‹€. μΆ”μΆœν•˜κ³ μž ν•˜λŠ” μ†ŒμŠ€ μ½”λ“œλ₯Ό μ„ νƒν•œ λ‹€μŒ, μ’ŒμΈ‘μ— λ‚˜νƒ€λ‚˜λŠ” 전ꡬ μ•„μ΄μ½˜μ„ ν΄λ¦­ν•˜κ±°λ‚˜ (⌘.)λ₯Ό 눌러 μ‚¬μš© κ°€λŠ₯ν•œ λ¦¬νŒ©ν† λ§ μ˜΅μ…˜μ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

    https://code.visualstudio.com/Docs/languages/typescript#_refactoring

// 클래슀
function Person(first, last) {
  // ν•¨μˆ˜λͺ… μ•„λž˜ ...이 λ‚˜νƒ€λ‚˜μ•Ό ν•˜λŠ”λ° μ•ˆ λ‚˜νƒ€λ‚¨
  this.first = first;
  this.last = last;
}

Person.prototype.getName = function () {
  return this.first + " " + this.last;
};

// ν”„λ‘œν† νƒ€μž…
class Person {
  first: string;
  last: string;

  constructor(frist: string, last: string) {
    this.first = first;
    this.last = last;
  }

  getName() {
    return this.first + " " + this.last;
  }
}

const marie = new Person("Marie", "Curie");
const personName = marie.getName();

var λŒ€μ‹  let/const μ‚¬μš©ν•˜κΈ°

  • varλ₯Ό let, const둜 λ³€κ²½ μ‹œ 일뢀 μ½”λ“œμ—μ„œ TS 였λ₯˜κ°€ λ°œμƒν•  수 μž‡λŠ”λ°, μŠ€μ½”ν”„ λ¬Έμ œκ°€ 생길 수 μžˆλŠ” μ½”λ“œμ΄λ‹ˆ μˆ˜μ •ν•˜λ©΄ λœλ‹€.
  • 쀑첩 ν•¨μˆ˜ ꡬ문에도 var의 κ²½μš°μ™€ λΉ„μŠ·ν•œ μŠ€μ½”ν”„ λ¬Έμ œκ°€ 쑴재
    • ν•¨μˆ˜ ν˜Έμ΄μŠ€νŒ…μ€ μ‹€ν–‰ μˆœμ„œλ₯Ό μ˜ˆμƒν•˜κΈ° μ–΄λ ΅κ²Œ λ§Œλ“€κ³  직관적이지 μ•ŠκΈ° λ•Œλ¬Έμ— ν•¨μˆ˜ 선언식 λŒ€μ‹  ν‘œν˜„μ‹μ„ μ“°μžλŠ” 의견

for(;;) λŒ€μ‹  for-of λ˜λŠ” λ°°μ—΄ λ©”μ„œλ“œ μ‚¬μš©ν•˜κΈ°

  • C μŠ€νƒ€μΌμ˜ for 루프: for(;;)
  • λͺ¨λ˜ μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ for-of 루프
    • for-of λ£¨ν”„λŠ” μ½”λ“œκ°€ 짧고 인덱슀 λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ§€λ„ μ•ŠκΈ° λ•Œλ¬Έμ— μ‹€μˆ˜λ₯Ό 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.
    • 인덱슀 λ³€μˆ˜κ°€ ν•„μš”ν•˜λ‹€? forEach λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν•¨μˆ˜ ν‘œν˜„μ‹λ³΄λ‹€ ν™”μ‚΄ν‘œ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜κΈ°

  • 일반 ν‘œν˜„μ‹ ν•¨μˆ˜μ˜ this 바인딩은 non-strict modeμ—μ„œλŠ” window, strict modeμ—μ„œλŠ” undefinedλ‹€.
  • ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” μ •μ˜λœ ν™˜κ²½μ˜ thisλ₯Ό μœ μ§€ν•œλ‹€. == μΌκ΄€λœ this 바인딩. 콜백 ν•¨μˆ˜ λ‚΄μ—μ„œ thisλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” 경우, ν™”μ‚΄ν‘œ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μž.
  • noImplicitThisλ₯Ό μ„€μ •ν•˜λ©΄? this 바인딩 κ΄€λ ¨ 였λ₯˜λ₯Ό ν‘œμ‹œν•΄μ€€λ‹€.
class Foo {
  method() {
    console.log(this);
    [1, 2].forEach(function (i) {
      console.log(this); // noImplicitThis 였λ₯˜ λ°œμƒ: 'this' implicitly has type 'any' because it does not have a type annotation.
    });
  }
  arrow() {
    console.log(this);
    [1, 2].forEach((i) => {
      console.log(this);
    });
  }
}
const f = new Foo();
f.method();
// strict λͺ¨λ“œ: Foo, undefined, undefined
// non-strict λͺ¨λ“œ: Foo, window, window
f.arrow();
// 항상 Foo, Foo, Foo

단좕 객체 ν‘œν˜„ compact object literal κ³Ό ꡬ쑰 λΆ„ν•΄ ν• λ‹Ή object de-structuring μ‚¬μš©ν•˜κΈ°

  • { x: x }λ₯Ό { x }와 같이 ν‘œν˜„
    • 가독성이 μ’‹κ³  μ‹€μˆ˜κ°€ 적닀.
  • ν™”μ‚΄ν‘œ ν•¨μˆ˜ λ‚΄μ—μ„œ 객체λ₯Ό λ°˜ν™˜ν•  λ•ŒλŠ” μ†Œκ΄„ν˜Έλ‘œ 감싸야 함 => ν•¨μˆ˜μ˜ κ΅¬ν˜„λΆ€μ—λŠ” λΈ”λ‘μ΄λ‚˜ 단일 ν‘œν˜„μ‹μ΄ ν•„μš”ν•˜κΈ° λ•Œλ¬Έμ—, μ†Œκ΄„ν˜Έλ‘œ κ°μ‹Έμ„œ ν‘œν˜„μ‹μœΌλ‘œ λ§Œλ“œλŠ” 것이닀.
["A", "B"].map((char, idx) => ({ char, idx }));
// [{char: "A", idx: 0}, {char: "B", idx: 1}]
  • 객체의 속성 쀑 ν•¨μˆ˜λ₯Ό μΆ•μ•½ν•΄μ„œ ν‘œν˜„ν•˜λŠ” 방법
const obj = {
  onClickLong: function (e) {
    // ...
  },
  onClickCompact(e) {
    // .. μΆ•μ•½ ν‘œν˜„!
  },
};
  • 객체 ꡬ쑰 λΆ„ν•΄
const { props } = obj;
const { a, b } = props;

// μ΄λ ‡κ²Œ 쀄일 경우 a와 bλŠ” λ³€μˆ˜λ‘œ μ„ μ–Έλ˜μ§€λ§Œ, propsλŠ” λ³€μˆ˜ 선언이 μ•„λ‹˜μ— 주의
const {
  props: { a, b },
} = obj;

// ꡬ쑰 λΆ„ν•΄ 문법 λ‚΄μ—μ„œ 기본값을 지정할 수 있음
const { a = "default" } = obj.props;

// νŠœν”Œμ²˜λŸΌ μ‚¬μš©ν•˜λŠ” λ°°μ—΄μ—μ„œ 유용
const point = [1, 2, 3];
const [x, y, z] = point; // λ³€μˆ˜μ— ν• λ‹Ή
const [, a, b] = point; // 첫 번째 μš”μ†ŒλŠ” λ¬΄μ‹œν•˜λŠ” 경우

// ν•¨μˆ˜ λ§€κ°œλ³€μˆ˜μ—λ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€
const points = [
  [1, 2, 3],
  [4, 5, 6],
];
points.forEach(([x, y, z]) => console.log(x + y + z)); // 6, 15

ν•¨μˆ˜ λ§€κ°œλ³€μˆ˜ κΈ°λ³Έκ°’ μ‚¬μš©ν•˜κΈ°

  • μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ ν•¨μˆ˜μ˜ λͺ¨λ“  λ§€κ°œλ³€μˆ˜λŠ” 선택적이며, λ§€κ°œλ³€μˆ˜λ₯Ό μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ undefined둜 κ°„μ£Όλ©λ‹ˆλ‹€.
function log2(a, b) {
  console.log(a, b);
}
log2(); // undefined undefined
  • λ§€κ°œλ³€μˆ˜μ— 기본값을 μ§€μ •ν•˜λ©΄ μ½”λ“œκ°€ κ°„κ²°ν•΄μ§ˆ 뿐만 μ•„λ‹ˆλΌ, 선택적 λ§€κ°œλ³€μˆ˜λΌλŠ” 것을 λͺ…ν™•νžˆ λ‚˜νƒ€λ‚΄λŠ” νš¨κ³Όλ„ 쀄 수 μžˆμŠ΅λ‹ˆλ‹€. 기본값을 기반으둜 νƒ€μž… 좔둠이 κ°€λŠ₯ν•˜κΈ° λ•Œλ¬Έμ—, λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ν•  λ•Œ λ§€κ°œλ³€μˆ˜μ— νƒ€μž…κ΅¬λ¬Έμ„ 쓰지 μ•Šμ•„λ„ λ©λ‹ˆλ‹€.
// μ˜›λ‚  방식
function paserNum(str, base) {
  base = base || 10;
  return parseInt(str, base);
}

// λͺ¨λ˜ μžλ°”μŠ€ν¬λ¦½νŠΈ
function parseNum(str, base = 10) {
  return parseInt(str, base);
}

μ €μˆ˜μ€€ ν”„λ‘œλ―ΈμŠ€λ‚˜ 콜백 λŒ€μ‹  async/await μ‚¬μš©ν•˜κΈ°

  • async와 await을 μ‚¬μš©ν•˜λ©΄ μ½”λ“œκ°€ κ°„κ²°ν•΄μ Έμ„œ λ²„κ·Έλ‚˜ μ‹€μˆ˜λ₯Ό 방지할 수 있고, 비동기 μ½”λ“œμ— νƒ€μž… 정보가 μ „λ‹¬λ˜μ–΄ νƒ€μž… 좔둠을 κ°€λŠ₯ν•˜κ²Œ ν•©λ‹ˆλ‹€.
// callbackκ³Ό ν”„λ‘œλ―ΈμŠ€
function getJSON(url: string) {
  return fetch(url).then((response) => response.json());
}
function getJSONCallback(url: string, cb: (result: unknown) => void) {
  // ...
}

// async/await
async function getJSON(url: string) {
  const response = await fetch(url);
  return response.json();
}

μ—°κ΄€ 배열에 객체 λŒ€μ‹  Mapκ³Ό Set μ‚¬μš©ν•˜κΈ°

function countWords(text: string) {
  const counts: { [word: string]: number } = {};
  for (const word of text.split(/[\s,.]+/)) {
    counts[word] = 1 + (counts[word] || 0);
  }
  return counts;
}
  • constructor의 μ΄ˆκΈ°κ°’μ€ Object.prototype에 μžˆλŠ” μƒμ„±μž ν•¨μˆ˜μ΄λ‹€. image
  • 객체의 ν”„λ‘œν† νƒ€μž… μ²΄μΈμ—μ„œ μƒμ†λœ 속성듀이 μ˜ˆμƒμΉ˜ μ•Šμ€ λ™μž‘μ„ 유발
    • λ°©μ§€ν•˜κΈ° μœ„ν•΄ Map을 μ‚¬μš©ν•  수 μžˆλ‹€.
function countWordsMap(text: string) {
  const counts = new Map<string, number>();
  for (const word of text.split(/[\s,.]+/)) {
    counts.set(word, 1 + (counts.get(word) || 0)); // 객체의 ν‚€κ°€ λ¬Έμžμ—΄λ‘œ μ œν•œ
  }
}
  • κ²°κ³Ό
    • Object.prototype의 속성듀과 μΆ©λŒν•  μœ„ν—˜μ΄ μ—†λ‹€. Map은 μˆœμ„œλ₯Ό 보μž₯ν•˜κ³ , μ„±λŠ₯ λ©΄μ—μ„œλ„ 더 효율적 image

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ— use strict 넣지 μ•ŠκΈ°

  • ES5μ—μ„œλŠ” 버그가 될 수 μžˆλŠ” μ½”λ“œ νŒ¨ν„΄μ— 였λ₯˜λ₯Ό ν‘œμ‹œν•΄μ£ΌλŠ” '엄격 λͺ¨λ“œ(strict mode)'κ°€ λ„μž…λ¨
    • TS의 μ•ˆμ •μ„± 검사 sanity checkκ°€ 엄격 λͺ¨λ“œλ³΄λ‹€ 훨씬 μ—„κ²©ν•˜λ‹€. (무의미)
  • alwaysStrict λ˜λŠ” strict μ˜΅μ…˜μ„ μ„€μ •ν•˜λ©΄? 엄격 λͺ¨λ“œλ‘œ μ½”λ“œλ₯Ό νŒŒμ‹±ν•˜κ³ , μƒμ„±λœ μžλ°”μŠ€ν¬λ¦½νŠΈμ— 'use strict'을 μΆ”κ°€ν•©λ‹ˆλ‹€.

TC39, JS ν‘œμ€€ν™” 단계

  • TC39λŠ” 맀년 μƒˆλ‘œμš΄ κΈ°λŠ₯을 λ°œν‘œν•˜κ³  있고, μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ ν‘œμ€€ν™” 4단계 쀑 3단계 μ΄μƒμ˜ κΈ°λŠ₯듀을 νƒ€μž…μŠ€ν¬λ¦½νŠΈ 내에 κ΅¬ν˜„ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

Stage 0: Strawman

아이디어가 μ œμ•ˆλ˜κ³  λ…Όμ˜λ˜λŠ” 초기 λ‹¨κ³„μž…λ‹ˆλ‹€. 아직 곡식적인 μ œμ•ˆμ΄ μ•„λ‹ˆλ©°, 아이디어 μˆ˜μ€€μ—μ„œ λ…Όμ˜λ©λ‹ˆλ‹€.

Stage 1: Proposal

μ œμ•ˆμ΄ κ³΅μ‹μ μœΌλ‘œ TC39에 제좜되고, λͺ…ν™•ν•œ 문제 μ •μ˜μ™€ μ†”λ£¨μ…˜μ΄ ν¬ν•¨λ©λ‹ˆλ‹€. μ œμ•ˆμ΄ λ°œμ „ν•  수 μžˆλŠ” ꡬ쑰λ₯Ό 가지며, ν”Όλ“œλ°±μ„ λ°›μ•„ κ°œμ„ λ©λ‹ˆλ‹€.

Stage 2: Draft

μ œμ•ˆμ΄ μ’€ 더 κ΅¬μ²΄ν™”λ˜κ³ , ꡬ체적인 λ¬Έμ„œμ™€ μ˜ˆμ œκ°€ ν¬ν•¨λ©λ‹ˆλ‹€. 이 λ‹¨κ³„μ—μ„œλŠ” μ œμ•ˆμ΄ μ‹€μ œλ‘œ κ΅¬ν˜„λ  κ°€λŠ₯성이 λ†’μ•„μ§‘λ‹ˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 이 단계뢀터 μƒˆλ‘œμš΄ κΈ°λŠ₯을 μ‹€ν—˜μ μœΌλ‘œ κ΅¬ν˜„ν•˜κΈ° μ‹œμž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Stage 3: Candidate

μ œμ•ˆμ΄ 거의 μ™„μ„±λœ μƒνƒœλ‘œ, 리뷰와 ν”Όλ“œλ°±μ„ 톡해 μ΅œμ’… 쑰정이 μ΄λ£¨μ–΄μ§‘λ‹ˆλ‹€. 이 λ‹¨κ³„μ˜ μ œμ•ˆμ€ λŒ€λΆ€λΆ„μ˜ 경우 ν‘œμ€€μœΌλ‘œ μ±„νƒλ©λ‹ˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 이 λ‹¨κ³„μ˜ κΈ°λŠ₯을 적극적으둜 κ΅¬ν˜„ν•˜μ—¬ μ‚¬μš©μžμ—κ²Œ μ œκ³΅ν•©λ‹ˆλ‹€.

Stage 4: Finished

μ œμ•ˆμ΄ μ΅œμ’…μ μœΌλ‘œ μŠΉμΈλ˜μ–΄ ν‘œμ€€μ— ν¬ν•¨λ©λ‹ˆλ‹€. 이 λ‹¨κ³„μ˜ κΈ°λŠ₯은 곡식 ECMAScript μ‚¬μ–‘μ˜ 일뢀가 λ©λ‹ˆλ‹€.

μš”μ•½

  • TS 개발 ν™˜κ²½μ€ λͺ¨λ˜ μžλ°”μŠ€ν¬λ¦½νŠΈλ„ μ‹€ν–‰ν•  수 μžˆμœΌλ―€λ‘œ, μ΅œμ‹  κΈ°λŠ₯듀을 적극적으둜 μ‚¬μš©ν•œλ‹€. μ½”λ“œ ν’ˆμ§ˆμ„ ν–₯μƒμ‹œν‚¬ 수 있고 νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ νƒ€μž… 좔둠이 λ‚˜μ•„μ§„λ‹€.
  • νƒ€μž…μŠ€ν¬λ¦½νŠΈ 개발 ν™˜κ²½μ—μ„œ μ»΄νŒŒμΌλŸ¬μ™€ μ–Έμ–΄ μ„œλΉ„μŠ€λ₯Ό μ‚¬μš©ν•΄ class, ꡬ쑰 λΆ„ν•΄, async/await κΈ°λŠ₯을 μ‰½κ²Œ 배울 수 μžˆλ‹€.
  • 'use strict'λŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈ 컴파일러 μˆ˜μ€€μ—μ„œ μ‚¬μš©λ˜λ―€λ‘œ μ½”λ“œμ—μ„œ 제거
  • TC39 κΉƒν—™ μ €μž₯μ†Œ, νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ 릴리슀 λ…ΈνŠΈλ₯Ό 톡해 μ΅œμ‹  κΈ°λŠ₯을 확인할 수 μžˆλ‹€.

μ•„μ΄ν…œ 59: νƒ€μž…μŠ€ν¬λ¦½νŠΈ λ„μž… 전에 @ts-check와 JSDoc을 μ‹œν—˜ν•΄ 보기

@ts-check

  • @ts-check μ§€μ‹œμžλ₯Ό μ‚¬μš©ν•˜λ©΄, νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ „ν™˜μ‹œμ— μ–΄λ–€ λ¬Έμ œκ°€ λ°œμƒν•˜λŠ”μ§€ 미리 μ‹œν—˜ν•΄ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
    • νƒ€μž… 체컀가 νŒŒμΌμ„ λΆ„μ„ν•˜κ³ , 발견된 였λ₯˜λ₯Ό λ³΄κ³ ν•˜λ„λ‘ μ§€μ‹œν•¨ - 맀우 λŠμŠ¨ν•œ μˆ˜μ€€μœΌλ‘œ νƒ€μž… 체크 μˆ˜ν–‰ν•˜λŠ”λ°, noImplicitAny 섀정을 ν•΄μ œν•œ 것보닀 체크λ₯Ό 덜 함

image

type.d.ts: ν”„λ‘œμ νŠΈ νƒ€μž… μ„ μ–Έ

  • μ„ μ–Έλ˜μ§€ μ•Šμ€ μ „μ—­ λ³€μˆ˜
// @ts-check
/// <reference path="./type.d.ts">
console.log(user.firstName);

// type.d.ts
interface UserData {
  firstName: string;
  lastName: string;
}
declare let user: UserData;

μ„œλ“œνŒŒν‹° 라이브러리

  • @ts-check μ§€μ‹œμžλ₯Ό μ‚¬μš©ν•˜λ©΄μ„œ, μ„œλ“œνŒŒν‹°μ˜ 라이브러리의 νƒ€μž… 선언을 μ„€μΉ˜ & ν™œμš©ν•΄μ„œ νƒ€μž… 체크λ₯Ό μ‹œν—˜ν•΄λ³Ό 수 μžˆλ‹€.

DOM μ—˜λ¦¬λ¨ΌνŠΈμ™€ JSDoc

  • DOM μ—˜λ¦¬λ¨ΌνŠΈ κ΄€λ ¨ 였λ₯˜
    • νƒ€μž… 단언, 리턴 νƒ€μž… μ •μ˜ λ“± TS κΈ°λŠ₯이기 λ•Œλ¬Έ
    • JSDoc을 μ‚¬μš©ν•˜μ—¬ νƒ€μž… 단언을 λŒ€μ²΄ν•  수 μžˆλ‹€.
// @ts-check
const ageEl = /** @type {HTMLInputElement} */ document.getElementById("age");
ageEl.value = "12";

λΆ€μ •ν™•ν•œ JSDoc

  • 이미 JSDoc μŠ€νƒ€μΌμ˜ 주석을 μ‚¬μš© μ€‘μ΄μ—ˆλ‹€λ©΄, @ts-check μ§€μ‹œμžλ₯Ό μ„€μ •ν•˜λŠ” μˆœκ°„λΆ€ν„° κΈ°μ‘΄ 주석에 νƒ€μž… 체크가 λ™μž‘ν•˜κ²Œ 되고 + 였λ₯˜ λ°œμƒ
    • 였λ₯˜λ₯Ό ν™•μΈν•˜κ³  μˆ˜μ •
  • Infer parameters types from usage
  • JS일 λ•Œ image
  • TS일 λ•Œ image image
  • 잘 λ™μž‘ν•˜μ§€ μ•ŠλŠ” κ²½μš°λ„ 있고, 주석이 μ½”λ“œ λΆ„λŸ‰μ„ λŠ˜λ €μ„œ λ‘œμ§μ„ ν•΄μ„ν•˜λŠ”λ° λ°©ν•΄κ°€ 될 수 μžˆμŠ΅λ‹ˆλ‹€.
  • νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” tsμ—μ„œ κ°€μž₯ 잘 λ™μž‘ν•œλ‹€. λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ˜ λͺ©ν‘œ => ts
  • λ‹€λ§Œ, 이미 JSDocs μ£Όμ„μœΌλ‘œ νƒ€μž… 정보가 많이 λ‹΄κ²¨μžˆλŠ” μ½”λ“œλΌλ©΄? @ts-check μ§€μ‹œμžλ‘œ νƒ€μž… 체컀λ₯Ό μ‹€ν—˜ν•΄μ„œ 초기 였λ₯˜λ₯Ό λΉ λ₯΄κ²Œ μž‘μ„ 수 μžˆλ‹€.

μš”μ•½

  • 파일 상단에 // @ts-checkλ₯Ό μΆ”κ°€ν•˜μ—¬ μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œλ„ νƒ€μž… 체크λ₯Ό μˆ˜ν–‰ν•  수 μžˆλ‹€.
  • μ „μ—­ μ„ μ–Έκ³Ό μ„œλ“œνŒŒν‹° 라이브러리의 νƒ€μž… 선언을 μΆ”κ°€ν•˜λŠ” 방법을 읡힐 수 있음
  • JSDoc 주석을 잘 ν™œμš©ν•˜λ©΄? JSμ—μ„œλ„ νƒ€μž… 단언과 μΆ”λ‘  ν•  수 있음
  • JSDoc은 쀑간 단계닀.

μ•„μ΄ν…œ 60: allowJs둜 TS와 JS 같이 μ‚¬μš©ν•˜κΈ°

  • allowJS μ˜΅μ…˜μ„ μ‚¬μš©ν•˜λ©΄, TS와 JSλ₯Ό μ„œλ‘œ importν•  수 μžˆλ‹€.
    • TSλŠ” JS의 μƒμœ„ 집합이닀~
    • λͺ¨λ“ˆ λ‹¨μœ„λ‘œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλ‘œ μ „ν™˜ν•˜λ©΄μ„œ, ν…ŒμŠ€νŠΈ μˆ˜ν–‰ν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμ— allowJs μ˜΅μ…˜μ΄ ν•„μš”
  • λ²ˆλ“€λŸ¬μ— TSκ°€ ν†΅ν•©λ˜μ–΄ μžˆκ±°λ‚˜, ν”ŒλŸ¬κ·ΈμΈ λ°©μ‹μœΌλ‘œ 톡합이 κ°€λŠ₯ν•˜λ‹€λ©΄? allowJs κ°„λ‹¨νžˆ 적용 κ°€λŠ₯
  • tsify, browserify
    $ browserify index.ts -p [ tsify --allowJs ] > bundle.js
    
  • jest.conifg.js
    module.exports = {
      transform: {
        "^.+\\.tsx?$": "ts-jest", // 전달할 νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ†ŒμŠ€ 지정
      },
    };
    
  • outDir μ˜΅μ…˜
    • νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ outDir에 μ§€μ •λœ 디렉토리에 μ†ŒμŠ€ Dirκ³Ό λΉ„μŠ·ν•œ ꡬ쑰둜 μžλ°”μŠ€ν¬λ¦½νŠΈ μ½”λ“œλ₯Ό μƒμ„±ν•˜κ²Œ 됨 => outDir둜 μ§€μ •λœ 디렉토리λ₯Ό λŒ€μƒμœΌλ‘œ κΈ°μ‘΄ λΉŒλ“œ 체인을 μ‹€ν–‰ν•˜λ©΄ 됨
    • κΈ°μ‘΄ μžλ°”μŠ€ν¬λ¦½νŠΈ κ·œμΉ™(target, module μ˜΅μ…˜)에 따라 좜λ ₯ μ˜΅μ…˜μ„ μ‘°μ •ν•΄μ•Ό ν•  수 있음

Things to Remember

  • Use the allowJs compiler option to support mixed JavaScript and TypeScript as you transition your project.
    • 점진적 λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ„ μœ„ν•΄, μžλ°”μŠ€ν¬λ¦½νŠΈμ™€ νƒ€μž…μŠ€ν¬λ¦½νŠΈλ₯Ό λ™μ‹œμ— μ‚¬μš©ν•  수 μžˆλŠ” allowJs μ˜΅μ…˜ μ‚¬μš©
  • Get your tests and build chain working with TypeScript before beginning large-scale migration.
    • λŒ€κ·œλͺ¨ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ 전에, ν…ŒμŠ€νŠΈμ™€ λΉŒλ“œ 체인에 νƒ€μž…μŠ€ν¬λ¦½νŠΈλ₯Ό μ μš©ν•œλ‹€.

μ•„μ΄ν…œ 61: μ˜μ‘΄μ„± 관계에 따라 λͺ¨λ“ˆ λ‹¨μœ„λ‘œ μ „ν™˜ν•˜κΈ°

  • 점진적 λ§ˆμ΄κ·Έλ ˆμ΄μ…˜
    • λͺ¨λ“ˆ λ‹¨μœ„λ‘œ 진행
  • λͺ¨λ“ˆ κ°„ μ˜μ‘΄μ„± 문제!
    • λ‹€λ₯Έ λͺ¨λ“ˆμ— μ˜μ‘΄ν•˜μ§€ μ•ŠλŠ” μ΅œν•˜λ‹¨ λͺ¨λ“ˆλΆ€ν„° μž‘μ—…μ„ μ‹œμž‘ν•΄μ•Ό ν•œλ‹€.
  • μ„œλ“œνŒŒν‹° 라이브러리
    • ν”„λ‘œμ νŠΈ --의쑴--> μ„œλ“œνŒŒν‹° 라이브러리
    • μ„œλ“œνŒŒν‹° 라이브러리 --의쑴x--> ν”„λ‘œμ νŠΈ
    • μ„œλ“œνŒŒμ΄ 라이브러리 νƒ€μž… 정보λ₯Ό λ¨Όμ € ν•΄κ²°ν•œλ‹€.
  • μ™ΈλΆ€ API의 νƒ€μž… 정보도 λ¨Όμ € ν•΄κ²°ν•œλ‹€.
    • μ™ΈλΆ€ API의 νƒ€μž… μ •λ³΄λŠ” λ¬Έλ§₯이 μ—†κΈ° λ•Œλ¬Έμ—, νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ μΆ”λ‘ ν•˜κΈ° μ–΄λ ΅λ‹€.
    • API 사양을 기반으둜 νƒ€μž… 정보λ₯Ό 생성해야 ν•œλ‹€.

μ˜μ‘΄μ„±

  • dependency graphλ₯Ό κ·Έλ €λ³Ό 수 μžˆλ‹€.
    • ν™”μ‚΄ν‘œ: μ˜μ‘΄μ„±
    • 짙은 μƒμž: μˆœν™˜ μ˜μ‘΄μ„± circular dependency
  • λŒ€λΆ€λΆ„μ˜ ν”„λ‘œμ νŠΈμ—μ„œ μ˜μ‘΄μ„± μ΅œν•˜λ‹¨μ— μœ ν‹Έλ¦¬ν‹° λͺ¨λ“ˆμ΄ μœ„μΉ˜ν•˜λŠ” νŒ¨ν„΄!
  • λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ν•  λ•ŒλŠ”? νƒ€μž… μ •λ³΄λ§Œ μΆ”κ°€ν•˜κ³ , λ¦¬νŒ©ν† λ§μ„ ν•˜λ©΄ μ•ˆλœλ‹€.
    • λ¦¬νŒ©ν† λ§ 사항은 λͺ©λ‘ν™” ν•˜κΈ°

μ„ μ–Έλ˜μ§€ μ•Šμ€ 클래슀 멀버

  • 클래슀 멀버λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ„ μ–Έν•˜κΈ° μœ„ν•΄μ„œ, IDE의 quick fix κΈ°λŠ₯을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • Declare property '~~'
    • Add all missing members

image

class Greeting {
  constructor(name) {
    this.greeting = "Hello";
    //   ~~~~~~~~ Property 'greeting' does not exist on type 'Greeting'
    this.name = name;
    //   ~~~~ Property 'name' does not exist on type 'Greeting'
  }
  greet() {
    return `${this.greeting} ${this.name}`;
    //             ~~~~~~~~         ~~~~ Property ... does not exist
  }
}
  • 잘λͺ»λœ 섀계
    • λ¦¬νŒ©ν† λ§ ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€!
    • λ°œκ²¬ν•˜λ©΄ κΈ°λ‘ν•˜κΈ°

νƒ€μž…μ΄ λ°”λ€ŒλŠ” κ°’

  • ν•œκΊΌλ²ˆμ— 객체λ₯Ό μƒμ„±ν•˜κΈ°
// js => ts
const state = {};
state.name = "New York";
//    ~~~~ Property 'name' does not exist on type '{}'
state.capital = "Albany";
//    ~~~~~~~ Property 'capital' does not exist on type '{}'

// ts
const state = {
  name: "New York",
  capital: "Albany",
}; // OK

// μž„μ‹œλ°©νŽΈ 단언문
// λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ΄ μ™„λ£Œλœ 이후, 문제λ₯Ό μ œλŒ€λ‘œ ν•΄κ²°ν•΄μ•Ό ν•œλ‹€!
interface State {
  name: string;
  capital: string;
}
const state = {} as State;
state.name = "New York"; // OK
state.capital = "Albany"; // OK
  • JSDocκ³Ό @ts-checkλ₯Ό μ‚¬μš©ν•΄ νƒ€μž… 정보λ₯Ό μΆ”κ°€ν•œ μƒνƒœλΌλ©΄, TS둜 μ „ν™˜ν•˜λŠ” μˆœκ°„ νƒ€μž… 정보가 'λ¬΄νš¨ν™”'λœλ‹€λŠ” 것에 주의 (tsμ—μ„œ λ™μž‘ν•˜μ§€ μ•ŠμŒ)
    • Annotate with type from JSDoc
      • JSDoc을 μ‚¬μš©ν•΄ νƒ€μž… ꡬ문을 μƒμ„±ν•œ ν›„, JSDoc μ‚­μ œ! image
  • λ§ˆμ§€λ§‰ 단계: ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό νƒ€μž…μŠ€ν¬λ¦½νŠΈλ‘œ μ „ν™˜
    • ν…ŒμŠ€νŠΈ μ½”λ“œλŠ” μ˜μ‘΄μ„± κ΄€κ³„λ„μ˜ μ΅œμƒλ‹¨μ— μœ„μΉ˜. λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ˜ λ§ˆμ§€λ§‰ 단계

Things to Remember

  • Start migration by adding @types for third-party modules and external API calls.
    • λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ˜ μ‹œμž‘ => μ„œλ“œνŒŒν‹° λͺ¨λ“ˆκ³Ό μ™ΈλΆ€ API콜
  • Begin migrating your own modules from the bottom of the dependency graph upwards. The first module will usually be some sort of utility code. Consider visualizing the dependency graph to help you track progress.
    • μ˜μ‘΄μ„± κ΄€κ³„λ„μ˜ μ•„λž˜λΆ€ν„° μœ„λ‘œ μ˜¬λΌκ°€λ©° λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ ν•œλ‹€. bottom to top~
    • μ΅œν•˜λ‹¨μ€ 보톡 μœ ν‹Έλ¦¬ν‹° μ½”λ“œλ‹€. μ˜μ‘΄μ„± 관계도λ₯Ό μ‹œκ°ν™”ν•˜λ©΄μ„œ 진행 과정을 μΆ”μ ν•˜λŠ” 것이 μ’‹λ‹€.
  • Resist the urge to refactor your code as you uncover odd designs. Keep a list of ideas for future refactors, but stay focused on TypeScript conversion.
    • μ΄μƒν•œ 섀계λ₯Ό λ°œκ²¬ν•΄λ„ λ¦¬νŒ©ν„°λ§ κΈˆμ§€. λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ— μ§‘μ€‘ν•œλ‹€. λ¦¬νŒ©ν„°λ§ μž¬λ£ŒλŠ” λͺ©λ‘μœΌλ‘œ λ§Œλ“ λ‹€.
  • Be aware of common errors that come up during conversion. Move JSDoc types into TypeScript type annotations if necessary to avoid losing type safety as you convert.
    • ν•„μš”μ— 따라 JSDoc νƒ€μž…μ„ νƒ€μž… ꡬ문으둜 λ³€κ²½ν•œλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈλ‘œ μ „ν™˜ν•˜λ©° λ°œκ²¬ν•˜λŠ” 일반적 였λ₯˜λ₯Ό λ†“μΉ˜μ§€ μ•Šλ„λ‘ ν•œλ‹€.

μ•„μ΄ν…œ 62: λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ˜ 완성을 μœ„ν•΄ noImplicitAny μ„€μ •ν•˜κΈ°

  • noImplicitAnyλ₯Ό μ„€μ •ν•˜μ—¬ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ™„λ£Œ
    • μ²˜μŒμ—λŠ” λ‘œμ»¬μ—λ§Œ μ˜΅μ…˜ μ„€μ •ν•˜κ³  μž‘μ—… (원격에 μ„€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ λΉŒλ“œ μ‹€νŒ¨κ°€ μ•ˆλœ¨κΈ° λ•Œλ¬Έ)
    • μˆ˜μ •ν•œ λΆ€λΆ„λ§Œ μ»€λ°‹ν•΄μ„œ 점진적 λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ ν•˜κΈ°!
  • νƒ€μž… 체컀가 λ°œμƒν•˜λŠ” 였λ₯˜μ˜ 개수 => noImplicitAny와 κ΄€λ ¨λœ μž‘μ—…μ˜ 진척을 λ‚˜νƒ€λ‚΄λŠ” μ§€ν‘œλ‘œ ν™œμš©
  • νƒ€μž… 체크의 강도 μ„œμ„œνžˆ 높이기
    • strictNullChecks => noImplicitAny => strict

Things to Remember

  • Don't consider your TypeScript migration done until you adopt noImplicitAny. Loose type checking can mask real mistakes in type declarations.
    • noImplictAny 섀정을 ν™œμ„±ν™”ν•˜μ—¬ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜μ˜ λ§ˆμ§€λ§‰ 단계λ₯Ό μ§„ν–‰ν•œλ‹€.
    • noImplictAny 섀정이 μ—†λ‹€λ©΄? νƒ€μž… μ„ μ–Έκ³Ό κ΄€λ ¨λœ μ‹€μ œ 였λ₯˜κ°€ λ“œλŸ¬λ‚˜μ§€ μ•ŠλŠ”λ‹€.
  • Fix type errors gradually before enforcing noImplicitAny. Give your team a chance to get comfortable with TypeScript before adopting stricter checks.
    • noImplictAnyλ₯Ό μ „λ©΄ μ μš©ν•˜κΈ° 전에, νƒ€μž… 였λ₯˜λ₯Ό μ μ§„μ μœΌλ‘œ μˆ˜μ •ν•œλ‹€.
    • μ—„κ²©ν•œ νƒ€μž… 체크λ₯Ό μ μš©ν•˜κΈ° 전에 νŒ€μ›λ“€μ΄ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ— μ΅μˆ™ν•΄μ§ˆ 수 μžˆλ„λ‘ ν•œλ‹€.