타입 선언 파일 작성 및 사용
앞서 8장 1절에서 .d.ts 파일이 무엇이고 왜 필요한지, 그리고 declare 키워드의 기본적인 사용법을 알아보았습니다. 이번 절에서는 실제 시나리오를 바탕으로 타입 선언 파일(.d.ts)을 어떻게 작성하고 프로젝트에서 사용하는지 구체적인 예시와 함께 살펴보겠습니다.
시나리오: 자바스크립트 유틸리티 라이브러리에 타입 추가하기
우리는 이미 존재하는 간단한 자바스크립트 유틸리티 파일이 있다고 가정하고, 이 파일에 타입스크립트의 타입 정보를 추가하는 .d.ts 파일을 작성해볼 것입니다.
src/js/StringUtils.js)
// 이 파일은 CommonJS 모듈로 작성되었다고 가정합니다.
/**
* 주어진 문자열의 첫 글자를 대문자로 변환합니다.
* @param {string} str - 원본 문자열
* @returns {string} 첫 글자가 대문자로 변환된 문자열
*/
function capitalize(str) {
if (typeof str !== 'string' || str.length === 0) {
return '';
}
return str.charAt(0).toUpperCase() + str.slice(1);
}
/**
* 주어진 문자열이 비어있는지 (null, undefined, 빈 문자열) 확인합니다.
* @param {string | null | undefined} str - 확인할 문자열
* @returns {boolean} 비어있으면 true, 아니면 false
*/
function isEmpty(str) {
return str === null || str === undefined || str.length === 0;
}
module.exports = {
capitalize,
isEmpty
};이 자바스크립트 파일은 capitalize와 isEmpty라는 두 함수를 CommonJS 방식으로 내보냅니다. 타입스크립트 프로젝트에서 이 함수들을 import하여 사용할 때, 타입 정보가 없으면 any로 추론되어 타입 검사의 이점을 누릴 수 없습니다.
타입 선언 파일 작성하기 (.d.ts)
이제 위 StringUtils.js 파일에 대한 타입 정보를 담은 StringUtils.d.ts 파일을 작성해봅시다. 이 파일은 src/js 폴더와 동일한 레벨에 생성하거나, 별도의 types 또는 @types 폴더에 생성할 수 있습니다. 여기서는 src/js/StringUtils.d.ts로 생성하겠습니다.
src/js/StringUtils.d.ts)
// 이 파일은 'StringUtils'라는 CommonJS 모듈에 대한 타입 선언 파일입니다.
// declare module '모듈 이름' { ... } 구문을 사용합니다.
declare module './StringUtils' {
/**
* 주어진 문자열의 첫 글자를 대문자로 변환합니다.
* @param str 원본 문자열
* @returns 첫 글자가 대문자로 변환된 문자열
*/
export function capitalize(str: string): string;
/**
* 주어진 문자열이 비어있는지 (null, undefined, 빈 문자열) 확인합니다.
* @param str 확인할 문자열
* @returns 비어있으면 true, 아니면 false
*/
export function isEmpty(str: string | null | undefined): boolean;
// 만약 기본 내보내기가 있다면 아래와 같이 선언할 수 있습니다.
// export default class StringUtil { ... }
}
// 참고: 만약 이 js 파일이 ES 모듈처럼 동작한다면, 다음과 같이 직접 export를 선언합니다.
// export function capitalize(str: string): string;
// export function isEmpty(str: string | null | undefined): boolean;declare module './StringUtils': 이 구문은 특정 모듈에 대한 타입 선언을 시작함을 의미합니다. './StringUtils'는 우리가 타입 정의를 제공하고자 하는 자바스크립트 모듈의 경로(또는 이름)입니다. TypeScript는 이 경로를 사용하여 해당 .js 파일과 이 .d.ts 파일을 연결합니다.
- 중요: 여기서 모듈 경로는
.js파일에서import또는require할 때 사용하는 경로와 일치해야 합니다..js확장자는 생략하는 것이 일반적입니다.
export function capitalize(str: string): string;: 자바스크립트 파일에서 module.exports.capitalize로 내보냈던 capitalize 함수의 타입 시그니처를 선언합니다. 실제 구현 없이 오직 함수 시그니처만 작성합니다. export 키워드를 사용하여 이 함수가 모듈 외부로 내보내진다는 것을 명시합니다.
export function isEmpty(str: string | null | undefined): boolean;: isEmpty 함수에 대해서도 동일하게 타입 시그니처를 선언합니다. 여기서는 str 매개변수가 string, null, undefined 중 하나일 수 있음을 유니온 타입으로 정확히 명시했습니다.
JSDoc 주석: JSDoc 주석(/** ... */)은 타입스크립트의 타입 정의 파일에서도 유용합니다. IDE나 에디터에서 함수 사용 시 툴팁으로 표시되어 개발자에게 자세한 정보를 제공합니다.
타입 선언 파일 사용하기
이제 app.ts 파일에서 StringUtils 모듈을 가져와 타입 안전하게 사용해봅시다.
src/app.ts)
import { capitalize, isEmpty } from './js/StringUtils'; // .d.ts 파일에 의해 타입 정보가 제공됨
const myString = "hello world";
const capitalizedString = capitalize(myString);
console.log(capitalizedString); // "Hello world"
const emptyString = "";
const isStringEmpty = isEmpty(emptyString);
console.log(`"${emptyString}" is empty: ${isStringEmpty}`); // "is empty: true"
const nullString: string | null = null;
const isNullEmpty = isEmpty(nullString);
console.log(`null is empty: ${isNullEmpty}`); // "is empty: true"
// 잘못된 타입의 인자 전달 시 컴파일 오류 발생
// const num = 123;
// const capitalizedNum = capitalize(num); // Error: 'number' 형식의 인수는 'string' 형식의 매개 변수에 할당될 수 없습니다.타입스크립트 컴파일러는 import { capitalize, isEmpty } from './js/StringUtils'; 구문을 만나면, 먼저 StringUtils.ts 파일을 찾습니다.
StringUtils.ts 파일이 없으면, StringUtils.d.ts 파일을 찾습니다.
StringUtils.d.ts 파일이 있으면, 해당 파일의 declare module './StringUtils' 블록 안에 정의된 capitalize와 isEmpty의 타입 시그니처를 읽어와 app.ts에서 사용되는 이 함수들에 대한 타입 정보를 제공합니다.
이 덕분에 capitalize(myString)와 같이 올바른 타입으로 함수를 호출하면 문제가 없지만, capitalize(num)처럼 잘못된 타입으로 호출하면 컴파일 타임에 오류를 잡아낼 수 있게 됩니다.
전역 타입 선언 (.d.ts 파일의 또 다른 용도)
특정 자바스크립트 코드가 모듈 시스템을 사용하지 않고 전역 스코프에 변수나 함수를 선언하는 경우(예: <script> 태그로 로드되는 레거시 코드나 브라우저 API), declare 키워드를 최상위 레벨에서 사용하여 전역 타입을 선언할 수 있습니다.
src/types/global.d.ts)
// 전역 변수 선언
declare var MY_APP_NAME: string;
declare const VERSION_NUMBER: number;
// 전역 함수 선언
declare function logActivity(message: string, level: 'info' | 'warn' | 'error'): void;
// 전역 인터페이스 확장 (예: Window 객체에 새로운 속성 추가)
interface Window {
myGlobalData?: {
userId: number;
sessionId: string;
};
}
// 또는 declare namespace를 사용한 전역 객체 선언
declare namespace Analytics {
function trackEvent(eventName: string, data?: object): void;
function setUserId(id: string): void;
}src/main.ts)
// 타입스크립트 컴파일러는 global.d.ts를 자동으로 인식합니다.
console.log(`Application Name: ${MY_APP_NAME}`);
console.log(`Version: ${VERSION_NUMBER}`);
logActivity("User logged in", "info");
if (window.myGlobalData) {
console.log(`User ID: ${window.myGlobalData.userId}`);
}
Analytics.trackEvent("page_view", { path: "/dashboard" });
// logActivity("Invalid level", "debug"); // Error: 'debug' 형식은 '"info" | "warn" | "error"' 형식에 할당될 수 없습니다.이러한 전역 선언 파일은 tsconfig.json의 include 설정에 의해 컴파일러가 인식할 수 있는 경로에 두기만 하면 자동으로 전역 스코프의 타입 정보를 제공합니다.
.d.ts 파일의 배포
직접 작성한 타입스크립트 라이브러리를 npm으로 배포할 때는, tsconfig.json의 declaration: true 옵션을 사용하여 컴파일 시 .js 파일과 함께 .d.ts 파일을 자동으로 생성하도록 설정합니다.
{
"compilerOptions": {
"declaration": true, // .d.ts 파일 자동 생성
"outDir": "./dist", // .js 및 .d.ts 파일 출력 디렉토리
// ...
}
}그리고 package.json 파일에 types (또는 typings) 필드를 추가하여, 라이브러리를 사용하는 다른 타입스크립트 프로젝트가 타입 정의 파일을 쉽게 찾을 수 있도록 경로를 명시합니다.
{
"name": "my-awesome-library",
"version": "1.0.0",
"main": "dist/index.js", // 실제 JS 구현 파일
"types": "dist/index.d.ts", // 타입 정의 파일
"files": [
"dist" // npm publish 시 dist 폴더 포함
]
}이렇게 설정하면, npm install my-awesome-library를 통해 라이브러리를 설치한 사용자들은 자동으로 타입 정보를 받아 타입 안전하게 라이브러리를 사용할 수 있습니다.
.d.ts 파일의 작성과 사용은 타입스크립트가 자바스크립트 생태계와 효과적으로 상호작용하는 핵심 방법입니다.
기존 자바스크립트 코드에 타입을 부여하거나 타입스크립트 라이브러리를 배포할 때, .d.ts 작성법을 능숙하게 다루는 것이 중요합니다.
declare module, export, 정확한 타입 시그니처 작성을 통해 코드 타입 안정성과 개발 경험을 크게 향상시킬 수 있습니다.