이펙티브 타입스크립트 (댄 밴더캄 지음) 를 읽고 정리
📍요약
✅전역 변수 사용을 지양하고, DOM에 데이터를 저장하면 안된다 (UI와 데이터를 분리)
✅내장 타입에 데이터를 저장해야 하는 경우, 안전한 타입 접근법 중 하나를 사용한다
- 타입 보강
- 확장하는 인터페이스를 만들고 단언
✅보강의 모듈 영역 문제를 이해해야 한다
📍몽키 패치 (Monkey Patch)
✅런타임에 기존 코드를 동적으로 변경하는 것
예시
// 전역 객체에 임의의 프로퍼티 추가
document.monkey = 'Tamarin';
window.monkey = "Howler";
console.log(document.monkey); // 'Tamarin'
console.log(window.monkey); // "Howler"
// 내장 객체의 프로퍼티를 변경하는것은 안되는 듯..
Math.PI = 3;
console.log(Math.PI); // 3.141592653589793
📍객체와 클래스에 임의의 프로퍼티 추가 가능
✅자바스크립트는 유연하게 객체나 클래스에 임의의 프로퍼티를 추가할 수 있다
- 또한 DOM에 데이터를 자유롭게 추가할 수도 있다
// JavaScript Code
document.monkey = 'Tamarin';
window.monkey = "Howler";
const el = document.getElementById("colobus")!;
el.home = "tree";
✅심지어 내장 객체의 프로토타입에도 프로퍼티 추가 가능
// JavaScript Code
// 정규표현식 객체의 프로토타입에 임의의 프로퍼티 추가
RegExp.prototype.monkey = "Capuchin" // "Capuchin"
// 정규표현식 인스턴스에서 프로토타입 객체를 상속받기 때문에
// 임의의 프로퍼티 참조 가능
/123/.monkey // "Capuchin"
✅window 또는 DOM node에 데이터를 추가하면, 그 데이터는 전역 변수가 된다
- 이는 코드 내에서 불필요한 의존성을 갖게 하여 사이드 이펙트를 초래할 수 있다
✅타입스크립트의 경우, 타입 체커가 Document와 HTMLElement의 내장 속성을 알지만, 임의로 추가한 속성은 몰라서 에러를 발생
document.monkey = 'Tamarin';
// ~~~~~~ Property 'monkey' does not exist on type 'Document'
window.monkey = "Howler";
// 타입 에러 : Window & typeof globalThis 에 monkey 프로퍼티 없음
const el = document.getElementById("colobus")!;
el.home = "tree";
// 타입 에러 : HTMLElement 에 home 프로퍼티가 없음
에러를 해결하려면 any 단언문을 사용할 수 있지만... 언어 서비스 기능을 이용할 수 없다
// any 단언문
(document as any).monkey = 'Tamarin'; // OK
// any 때문에 언어 서비스 기능 비활성화
(document as any).monky = 'Tamarin'; // Also OK, 오타 감지 기능 비활성화
(document as any).monkey = /Tamarin/; // Also OK, 타입 감지 기능 비활성화
2가지 대안이 존재한다
📍1. Type 보강(Augmentation)
✅타입 보강을 통해 기존의 Document 인터페이스를 확장
interface Document {
monkey: string;
}
document.monkey = 'Tamarin'; // OK
document.monke = 'Tamarin'; // 타입에러 : 오타 감지 기능 활성화
✅장점
1. 타입 안정성
2. 언어 서비스 기능 활성화 (오타, 주석, 자동완성 등)
3. 몽키 패치가 어떤 부분에 적용됐는지 기록 남음
esmodule 을 지원하게 하려면 global 선언을 추가해야 한다
export {};
declare global {
interface Document {
monkey: string;
}
}
document.monkey = 'Tamarin'; // OK
✅하지만, 보강은 전역적으로 사용되기 때문에, 이 부분을 분리할 수 없다는 문제가 있다
더 정확하게 사용하려면...
📍2. 구체적인 타입 단언문
Document 인터페이스를 확장하는 MonkeyDocument 인터페이스를 만들고 document에 단언
interface MonkeyDocument extends Document {
monkey: string;
}
(document as MonkeyDocument).monkey = 'Macaque';
✅장점
⭐모듈 영역 문제를 해결 (import 하는 곳에서만 사용할 수 있다)