class NumberClass {
private readonly id: number;
constructor(id: number){
this.id = id;
}
toString():string{
return `${typeof this.id}`
}
}
export function main(){
const stringNumber:any = "124214";
const numClass = new NumberClass(stringNumber);
console.log(numClass.toString());
}
main();
위 코드를 실행 했을 때 어떤 값이 출력될 것 같나요?
string? number?
정답은 string 입니다.
왜일까요? 바로 제목 그대로 any 타입의 함정입니다.
any 타입의 특징은 아래와 같습니다
해당 값에 대해서 임의의 속성 접근 가능
함수처럼 호출 가능
다른 임의 타입의 값 할당 가능
타입 검사 하지 않음
4 번째의 특징에 따라 컴파일 에러에 잡히지 않습니다.
컴파일 후 js 코드로 본다면 이유에 대해 더 명확하게 확인할 수 있습니다.
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = main;
var NumberClass = /** @class */ (function () {
function NumberClass(id) {
this.id = id;
console.log(typeof id, typeof this.id);
}
NumberClass.prototype.toString = function () {
return "".concat(this.id);
};
return NumberClass;
}());
function main() {
var stringNumber = "124214";
var numClass = new NumberClass(stringNumber);
console.log(numClass.toString());
}
main();
JS에서는 더이상 타입체크를 하지 않기 때문에 불안전한 코드가 생겨버렸고 의도치 않은 동작 혹은 런타임 에러가 발생할 가능성이 생겼습니다.
이를 방지하기 위해 가장 중요한 것은 any를 사용하지 않는 것입니다.
만일 명시적으로 타입을 선언했다고 가정해봅시다.
파라미터로 사용된 stringNumber
변수에 명시적으로 타입을 선언했지만 해당 변수에 대입되는 변수의 타입이 any라면 이는 안전하지 않는 코드가 됩니다.
그렇다고 any
를 아예 안쓸 수는 없을겁니다. 외부 API에 의해서 타입이 명시되어 있지 않을 수도 있고 갑자기 타입이 변경되는 등 타입이 모호할 경우가 있습니다.
이를 위해서 unknown
으로 대체할 수 있습니다.
unknown 타입은 TypeScript 3.0 에서 새롭게 출시된 any 대응 타입입니다.
unknown의 특징은 아래와 같습니다.
unknown
과 any
타입 외의 타입(string
, number
, boolean
, Function
, object
…)에서는 할당 불가능 그렇기 때문에 그 외 타입에 할당하기 위해서는 명시적으로 형 변환을 시켜줘야 합니다.
이처럼 Type Checking 과 동시에 Error Handling을 할 수 있게 되어 안전한 코드가 되었습니다.
추가로 tsconfig 에서 noImplictAny 옵션과 eslint no-explicit-any 옵션을 활용하여 any 사용을 방지할 수 있습니다.
위와 다른 케이스로 Nest.js 에서 @Req
, @Query
,@Param
데코레이터를 이용하여 값을 불러올 때 마찬가지로 string 형 number 가 number로 타입 변환이 제대로 이루어지지 않을 때가 있습니다.
이럴 때 해결 방법 중 하나는 ValidationPipe
의 trsnform 옵션을 사용할 경우 올바르게 타입 변환이 가능합니다.