지난번에 JSDoc으로 더 나은 주석 작성하기, 그리고 개발 관련 도서 추천이라는 글을 썻었는데요. JSDoc으로 함수, 클래스, 변수에 설명을 추가할 뿐만 아니라 타입을 지정해 주어 쉽게 파악이 가능한 주석을 작성하는 법을 포스팅했습니다. 오늘은 이 JSDoc을 이용하여 ts 파일이 아닌 js 파일에서도 타입을 검사할 수 있게 만드는 법을 포스팅하겠습니다. 더불어 타입스크립트처럼 사용할 수 있도록 제넥릭과 타입 캐스팅 같이 조금 더 심화된 사용법을 설명해드리겠습니다.
JS 파일에서 타입검사하기
// @ts-check
파일의 최상단에 위와 같은 키워드를 붙이면 js 파일에서도 타입스크립트와 같은 타입검사가 가능합니다. 이 키워드를 사용한다면 JavaScript 파일에 타입 정보와 주석을 추가하여 코드의 타입 오류를 발견하고, IDE 또는 에디터에서 타입 검사를 수행할 수 있도록 합니다. 그리고 이 기능은 JSDoc을 사용할 때 타입스크립트 못지 않은 타입 검사를 할 수 있습니다. 물론 타입스크립트의 타입 검사기를 사용하기에 JSDoc에서는 사용이 어려운 타입스크립트의 고급 기능 등 더 정확한 검사를 위해서는 타입스크립트 설치와 ts 파일에서 사용하는 것이 나을 수도 있습니다.
제네릭
제네릭은 코드의 재사용성을 높이고 다양한 타입을 지원할 수 있도록 해주는 중요한 도구입니다. JSDoc에서도 제네릭 타입을 정의하고 사용할 수 있으며, Object
등 기본적인 제네릭을 제공합니다.
제네릭 타입을 만들어서 사용하기
JSDoc에서는 제네릭 함수를 정의할 때, @template 태그를 사용하여 제네릭 타입 매개변수를 지정할 수 있습니다.
/**
* 주어진 타입을 그대로 반환하는 제네릭
* @template T
* @typedef {Object} Container
* @property {T} value
*/
다음 코드를 살펴보겠습니다.
/**
* 두 값을 더하는 함수에 사용할 제네릭 콜백 타입
* string 또는 number 타입만 허용
* @template T
* @callback AddGeneric
* @param {T} value1 - 더할 값 1
* @param {T} value2 - 더할 값 2
* @returns {T} - 더한 결과
*/
먼저 처음 보는 @callback
태그는 @typedef
와 비슷하지만 함수의 타입 지정울 뜻합니다. 따라서 @param
은 객체의 속성이 아닌 함수의 매개변수(parameter)를 뜻합니다. 이 @callback
태그는 AddGeneric라는 이름으로 지정되어 있고 value1과 value2는 같은 속성이고 그 두 값을 더한 결과를 리턴합니다. 자바스크립트에서는 문자열 간에 덧셈 연산이 가능하니 제네릭 T는 string과 number만 들어올 수 있게 해야합니다. 따라서 string | number
를 @template
키워드에 타입 지정해주었습니다.
@template과 @callback 태그를 함께 사용할 때는 @template이 @callback보다 위에 있거나 @callback의 return 이후에 있어야합니다.
사실 @callback
의 함수 타입 지정은 @typeof
에서도 사용할 수 있습니다. 훨씬 짧은 코드로 같은 기능을 수행할 수 있습니다.
/**
* 두 값을 더하는 함수에 사용할 제네릭 타입
* @template {string | number} T
* @typedef {(value3: T, value4: T) => T} AddGeneric2
*/
이렇게 만들어진 함수 타입은 다음과 같이 사용할 수 있습니다.
/**
* 두 숫자를 더하는 함수
* @type {AddGeneric<number>}
*/
function add(value1, value2) {
return value1 + value2;
}
이제 이 함수의 타입은 숫자를 더하는 함수가 되었습니다. 만약 AddGeneric<number> 대신
AddGeneric`을 사용한다면 문자열을 더하는 함수가 됩니다.
함수에 바로 사용하기
@typedef
나 @callback
을 사용하지 않고 함수나 클래스를 선언하면서 바로 제네릭을 사용하여도 아무 문제가 없습니다.
/**
* @template T
* @param {T} value1
* @param {T} value2
*/
function add2(value1, value2) {
if (typeof value1 === "string" && typeof value2 === "string") {
return value1 + value2;
} else if (typeof value1 === "number" && typeof value2 === "number") {
return value1 + value2;
}
throw new Error("더할 수 없는 요소");
}
타입 단언(Type Assertion)
타입스크립트를 사용하다가 JSDoc에 익숙해지기 위해 일부러 JSDoc의 기능만을 이용하여 타입을 검사할 때, 다음과 같은 문제점에 부딪혔습니다.
// @ts-check
let a = [1, 2, "3", "4"];
const numSum = a[0] + a[1];
분명 a의 0번 인덱스와 1번 인덱스는 모두 숫자인데 두 수의 합을 구하려고 하면 오류가 나게 됩니다. 타입스크립트였다면 as
키워드를 통해 타입 단언을 하였겠지만, JSDoc을 js 파일에서 사용한다면 주석 바깥의 부분은 일반 자바스크립트와 다를 바없습니다. 그래서 as
나 !
와 같은 간편한 타입단언을 지원하지 않습니다. 대신 조금 더 번거로운 방법의 타입 단언을 사용해야 합니다.
const numSum = /** @type {number} */ (a[0]) + /** @type {number} */ (a[1]);
변수를 처음 만들 때와 같은 방식으로 @type
태그를 사용하여 타입 단언을 할 수 있습니다. 이때 주의해야 할 점은 단언할 타입을 꼭 소괄호로 묶어주어야 한다는 것입니다. 그렇지 않으면 타입 단언이 이루어지지 않습니다.
몇 가지 주의할 점
object
와 Object
및 {}
을 타입으로 지정할 때 객체만을 뜻하지는 않습니다.
// @ts-check
/** @type {object} */
let object;
object = {"d": 2};
object = "string";
object = 1;
위의 코드는 // @ts-check를 이용하였음에도 어떠한 오류도 발생하지 않습니다. 확인해보면 object 변수는 any
로 지정되어 사용시 주의가 필요합니다. 만약 객체인 타입이 필요하다면 객체 키-값이 정의된 타입을 만들거나 키와 값의 타입을 알수 없다면 대안으로 { [key: string]: any }
와 같이 사용하는 것이 좋습니다.
'개발 > JavaScript' 카테고리의 다른 글
태스크큐를 중점으로 자바스크립트 코드의 실행 순서를 알아보자 (0) | 2024.09.25 |
---|---|
자바스크립트 제너레이터(Generator)로 반복가능한 객체 이터레이터(iterator)를 만들자(feat. 파이썬의 range 구현하기) (0) | 2024.07.28 |
package.json 알아보기(feat. Semantic Versioning & Peer Dependencies) (0) | 2024.06.23 |
JavaScript의 렉시컬 환경과 클로저, 조금 더 자세하게 알아보자 (0) | 2024.06.10 |
JSDoc으로 더 나은 주석 작성하기, 그리고 개발 관련 도서 추천 (0) | 2024.06.03 |