결론적으로 Reference type vs Primitive type 의 차이이다.
비슷해보이지만 완전히 다른것이며, Typescript는 둘을 엄격히 구분하기 때문에 절대 혼용해서 사용하면 안된다.
string과 number는 TypeScript의 원시타입(Primitive Type)이다.
typeof 123 === "number" 비교시 true를 반환
let age: number = 30;
String과 Number는 Javascript의 래퍼 객체(클래스)이며 참조타입(Reference Type)이다.
let age: Number = new Number(30);
typeof new Number(30) === "object" 비교시 true를 반환
그렇기 때문에, 아래처럼 혼용하여 사용할 경우 정상적인 비교를 수행 할 수 없게된다.
let a: number = 123;
let b: Number = new Number(123);
console.log(a === b); // false ❗❗❗
그러므로 String과 Number 의 사용은 권장되지 않으며,
실무에서는 가급적 string, number 타입의 사용이 권장된다.
| 구분 | 예시 | 설명 |
|---|---|---|
| 원시 타입 (Primitive Type) | number, string, boolean | TypeScript 전용 타입. 컴파일 타임에서만 존재 |
| 래퍼 객체 (Wrapper Object) | Number, String, Boolean JavaScript | 내장 생성자 함수. 런타임 객체로 존재 |
Javascript 객체(런타임 참조) 를 요구하는 기능들은 number 같은 원시타입 대신 Number 같은 참조타입을 써야할 경우가 있다.
NestJS의 @nestjs/swagger가 대표적이다.
@ApiParam({ name: 'movie_id', type: Number }) // ✅ 정상 동작
@ApiParam({ name: 'movie_id', type: number }) // ❌ 오류 발생
number와 Number의 차이를 다시 되짚어보면,
number는 Typescript의 타입 문법이므로 컴파일 타임 전용이지만,
Number는 Javascript의 런타임 객체이므로 실제로 존재하는 클래스이자 함수이다.
Swagger 데코레이터인 @ApiParam()은 내부적으로 런타임 메타데이터를 추출하여 Swagger 명세(Spec)을 만드는데, 여기서 type은 컴파일 타입이 아닌 클래스나 함수 객체를 요구한다.
그렇기 때문에 number가 아닌 Number를 써야한다.
Since TypeScript does not store metadata about generics or interfaces, when you use them in your DTOs, SwaggerModule may not be able to properly generate model definitions at runtime.
출처: https://docs.nestjs.com/openapi/types-and-parameters
컴파일 타임에는 TypeScript의 number, string 같은 원시 타입을 사용런타임에 메타데이터를 추출해야 하는 상황(예: Swagger의 @ApiParam) 에서는 Number, String 같은 JavaScript 생성자 함수(래퍼 객체)를 사용.