TypeScript는 구현과 별도로 타입 형태를 선언할 수 있습니다.
타입 선언은 .d.ts 확장자로 끝나는 선언 파일에 작성됩니다.
선언 파일은 일반적으로 프로젝트 내에서 작성되고, 프로젝트의 컴파일된 npm 패키지로 빌드 및 배포되거나 독립 실행(standalone) typings 패키지로 공유됩니다.
.d.ts 선언 파일은 런타임 코드를 포함할 수 없다는 제약 사항을 제외하고는 .ts 파일과 유사하게 작동합니다.
// types.d.ts
export interface Character {
catchphrase?: string;
name: string;
}
import { Character } from './types';
export const character: Character = {
catchphrase: 'Yee-haw',
name: 'Sandy Cheeks',
};
Tip
- 선언 파일은 값이 아닌 타입만 선언할 수 있는 코드 영역을 의미하는 앰비언트 컨텍스트(ambient context)를 생성합니다.
<script>
태그 같은 일부 외부 작업이 특정 타입의 이름을 사용해 값을 생성했음을 타입 시스템에 알립니다.// types.d.ts
// Ok
declare let declared: string;
// Error: Initializer are not allowed in ambient contexts.
declare let initializer: string = 'Wanda';
// fairies.d.ts
// Ok
declare function canGrantWish(wish: string): boolean;
// Error: An implementation cannot be declared in ambient contexts.
declare function grantWish(wish: string) {
return true;
}
class Fairy {
// Ok
canGrantWish(wish: string): boolean;
// Error: An implementation cannot be declared in ambient contexts.
grantWish(wish: string) {
return true;
}
}
Tip
- TypeScript의 암시적 any 타입 규칙은 일반 소스 코드와 마찬가지로 앰비언트 컨텍스트에 선언된 함수와 변수에 대해 동일하게 작동합니다.
- 앰비언트 컨텍스트에 선언된 함수나 변수가 any 타입이 되는 것을 막기 위해 명시적 type annotation을 사용합니다.
// index.ts
declare const myGlobalValue: string;
console.log(myGlobalValue); // Ok
// index.d.ts
// Ok
interface Writer {}
// Ok
declare interface Writer {}
// Ok, fullName의 타입은 원시 타입 string입니다.
declare const fullName: string;
// Ok, fullName의 타입은 리터럴 값 'Liz'입니다.
declare const fullName: 'Liz';
// Error: Tol-level declarations in .d.ts files must
// start with either 'declare' or 'export' modifier.
const lastName = 'Lemon';
// global.d.ts
declare const version: string;
// verstion.ts
export function logVersion() {
console.log(`Version: ${version}`); // Ok
}
Tip
- .d.ts 파일에 선언된 전역 타입에 자동으로 접근할 수 없는 경우 .d.ts 파일에서 import나 export 문이 있는지 다시 확인해야 합니다.
<script type="text/javascript">
window.myVerstion = "3.1.1"
</script>
// index.ts
export function logWindowVersion() {
console.log(`Window version is: ${window.myVersion}`);
window.alert('Built-in window types still work! Hooray!');
}
// types/data.d.ts
export interface Data {
version: string;
}
// types/global.d.ts
import { Data } from './types/data';
declare global {
const golballyDeclared: Data;
}
declare const locallyDeclared: Data;
// index.ts
import { Data } from './types/data';
function logData(data: Data) {
// Ok
console.log(`Data version is: ${data.version}`);
}
// Ok, import 문 없이 globallyDeclared 변수에 접근할 수 있습니다.
logData(globallyDeclared);
// Error: Cannot find name 'locallyDeclared'
logData(locallyDeclared);
모든 JavaScript 런타임에 존재하는 Array, Function 같은 내장된 전역 객체는 lib.[target].d.ts 파일 이름으로 선언됩니다.
내장된 라이브러리 선언 파일 또는 lib 파일은 JavaScript의 내장된 API 전체를 나타내기 때문에 상당히 큽니다.
// lib.es5.d.ts
interface Array<T> {
/**
배열의 길이를 가져오거나 설정합니다.
배열의 가장 큰 인덱스보다 1이 더 큰 숫자입니다.
**/
length: number;
// ...
}
TypeScript는 기본적으로 tsc CLI 또는 프로젝트의 tsconfig.json(기본값 es5)에서 제공된 target 설정에 따라 적절한 lib 파일을 포함합니다.
TypeScript 프로젝트는 target으로 지정한 JavaScript 버전의 모든 최소 버전 lib을 포함합니다.
Tip
- target보다 최신 버전의 JavaScript에서만 사용할 수 있는 기능은 타입 시스템에서 사용할 수 없습니다.
// lib.dom.d.ts
interface Storage {
// 키/값 쌍의 수를 반환합니다.
readonly length: number;
// 모든 키/값 쌍을 제거합니다.
clear(): void;
/*
주어진 키에 연결된 현재값을 반환하거나
주어진 키가 존재하지 않는 경우 null을 반환합니다.
*/
getItem(key: string): string | null;
}
// modules.d.ts
declare module 'my-example-lib' {
export const value: string;
}
// index.ts
import { value } from 'my-example-lib';
// Ok
console.log(value);
*
와일드카드를 포함해 해당 패턴과 일치하는 모든 모듈을 나타낼 수 있습니다. {[i: string]: string}
타입의 객체를 내보내는 *.module.css와 같은 패턴으로 모듈을 정의합니다.// styles.d.ts
declare module '*.module.css' {
const styles: { [i: string]: string };
export default styles;
}
import styles from './styles.module.css';
style.anyClassName; // 타입 : string
// index.ts
export const greet = (text: string) => {
console.log(`Hello, ${text}`);
};
// index.d.ts
export declare const greet: (text: string) => void;
// index.js
export const greet = (text) => {
console.log(`Hello, ${text}`);
};
lib/
index.js
index.d.ts
package.json
// package.json
{
"devDependencies": {
"jest": "^32.1.0"
}
}
// node_modules/@jest/globals/index.d.ts
export function describe(name: string, test: () => void): void;
export function it(name: string, test: () => void): void;
// node_modules/jest/index.d.ts
import * as global from '@jest/globals';
declare global {
const describe: typeof globals.describe;
const it: typeof globals.it;
}
{
"author": "Pendant Publishing",
"main": "./lib/index.js",
"name": "coffeetable",
"types": "./lib/index.d.ts",
"version": "0.5.22",
}
Tip
- types 필드가 패키지의 package.json에 존재하지 않으면, TypeScript는 ./index.d.ts를 기본값으로 지정합니다.
Note
- @types는 dependencies 또는 devDependencies로 설치하지만, 최근 몇 년 동안 이 둘의 구분은 모호해졌습니다.
- 프로젝트가 npm 패키지로 배포되어야 하는 경우, dependencies를 사용해야만 패키지를 사용하는 곳에서 사용되는 타입 정의를 가져올 수 있습니다.
- 프로젝트가 서버에서 빌드 및 실행되는 독립 실행형 애플리케이션이라면 devDependencies를 사용해 타입이 단지 개발 시 사용되는 툴임을 전달해야 합니다.
// package.json
{
"dependencies": {
"@types/lodash": "^4.14.182",
"lodash": "^4.17.21"
}
}
// package.json
{
"dependencies": {
"react": "^18.1.0"
},
"devDependencies": {
"@types/react": "^18.0.9"
}
}
가장 인기 있는 JavaScript 패키지는 자체 타이핑과 함께 제공되거나 DefinitelyTyped를 통해 타이핑할 수 있습니다.
아직 사용 가능한 타입이 없는 패키지에서 타입을 얻는 일반적인 세 가지 옵션은 다음과 같습니다.