TypeScript 3.5 strictBindCallApply로 bind 타입 안정성 확보
문제 상황
이벤트 핸들러를 클래스 메서드로 바인딩하는 코드에서 타입 체크가 무용지물이었다.
class EventManager {
handleClick(event: MouseEvent, data: string) {
console.log(data.toUpperCase());
}
init() {
// bind 후 타입이 any로 추론되어 잘못된 인자 전달 감지 못함
const handler = this.handleClick.bind(this, 123); // 런타임 에러!
document.addEventListener('click', handler);
}
}
data 파라미터에 숫자를 넘겨도 컴파일 단계에서 잡히지 않았다. 프로덕션에서 toUpperCase is not a function 에러가 발생했다.
TypeScript 3.5 업그레이드
TypeScript 3.5에서 추가된 strictBindCallApply 옵션이 이 문제를 해결한다.
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"strictBindCallApply": true
}
}
strict: true에 이미 포함되어 있지만, 명시적으로 표기했다.
수정 결과
class EventManager {
handleClick(event: MouseEvent, data: string) {
console.log(data.toUpperCase());
}
init() {
// 이제 타입 에러 발생
const handler = this.handleClick.bind(this, 123);
// Error: Argument of type 'number' is not assignable to parameter of type 'string'
}
}
컴파일 타임에 에러를 잡을 수 있게 되었다.
팀 적용 과정
전체 프로젝트에 적용하니 기존 코드에서 12곳의 타입 에러가 발견되었다. 대부분 이벤트 핸들러 바인딩이었고, 일부는 유틸 함수의 call() 사용 부분이었다.
// 발견된 문제 중 하나
function formatPrice(this: { currency: string }, price: number) {
return `${this.currency}${price}`;
}
const context = { currency: '$' };
formatPrice.call(context, '100'); // 이제 타입 에러
모든 케이스를 수정하는 데 하루 정도 소요되었지만, 잠재적 런타임 에러를 사전에 제거할 수 있었다.
정리
TypeScript 3.5로 업그레이드하면서 strictBindCallApply를 활성화했다. 기존 코드에서 숨어있던 타입 불일치 문제들을 찾아낼 수 있었고, 프로덕션 안정성이 개선되었다. TypeScript의 strict 옵션들은 초기 작업량이 늘어나지만 결국 유지보수 비용을 줄여준다.