타입 선언 파일 작성 및 사용
앞서 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
, 그리고 정확한 타입 시그니처 작성을 통해 코드의 타입 안정성과 개발 경험을 크게 향상시킬 수 있습니다.