Другое

Понимание файлов объявлений TypeScript (.d.ts) для разработчиков

Узнайте, как файлы объявлений TypeScript (.d.ts) взаимодействуют с .js и .ts, как правильно их использовать, их связь с типами и лучшие практики типобезопасности в JavaScript‑проектах, включая примеры.

Понимание файлов объявлений TypeScript (.d.ts)

Я пытаюсь разобраться с файлами объявлений TypeScript (.d.ts). Слышал, что они похожи на заголовочные файлы (.h) в C и C++, но, похоже, работают не совсем так. Мне тяжело понять, как правильно использовать .d.ts файлы.

Похоже, я не могу напрямую включать свои .js или .ts файлы в .d.ts файлы, что подразумевает, что в проекте нужны все три типа файлов. У меня есть несколько вопросов:

  1. Какова взаимосвязь между файлами .d.ts, .js и .ts?
  2. Как правильно использовать .d.ts файлы? Могу ли я навсегда удалить .ts файл, если у меня есть соответствующий .d.ts файл?
  3. Если я могу использовать .d.ts файлы вместо .ts файлов, как .d.ts файл узнает, какой JavaScript файл ему соответствует?

TypeScript declaration files (.d.ts) are type definition files that provide type information for JavaScript code without containing implementation details. Unlike C/C++ header files, .d.ts files work alongside JavaScript files rather than being included in them, enabling TypeScript to understand and provide type safety for existing JavaScript code and third-party libraries. The relationship between these file types creates a system where TypeScript can check types during development while producing clean JavaScript output for runtime execution.

Contents

What Are TypeScript Declaration Files?

TypeScript declaration files (.d.ts) are special files that contain type information and interfaces without any implementation code. These files serve as type definitions that tell TypeScript what types, interfaces, functions, classes, and modules are available in your JavaScript code.

Declaration files contain only type information and no executable code. They are used to describe the shape of JavaScript code that exists elsewhere.

Unlike C/C++ header files which are included and compiled into the final binary, TypeScript declaration files work differently. They are processed by the TypeScript compiler to provide type checking during development but are completely removed during compilation, leaving only the actual JavaScript implementation.

According to Wikipedia’s TypeScript documentation, “Type declarations for these libraries are usually provided with the source code but can be declared or installed separately if needed.” This highlights that declaration files can either come bundled with libraries or be added separately when working with JavaScript-only libraries.

Relationship Between .d.ts, .js, and .ts Files

The relationship between these three file types is fundamental to understanding how TypeScript works:

.ts Files (TypeScript Source)

  • Contain both type annotations and implementation code
  • Are compiled by the TypeScript compiler (tsc) into JavaScript
  • Can be written with strict type checking enabled
  • Are the primary way to write new TypeScript applications

.js Files (JavaScript Source)

  • Contain only implementation code (no types)
  • Are executed directly by JavaScript engines
  • Can be used in TypeScript projects with declaration files

.d.ts Files (Declaration Files)

  • Contain only type information and interfaces
  • Have no implementation code
  • Are processed by TypeScript compiler but not compiled to output
  • Describe the shape and types of existing JavaScript code

The key difference from C/C++ header files is that .d.ts files are not included or merged with implementation files. Instead, they work alongside JavaScript files to provide type information during development.

According to Wikipedia’s TypeScript documentation, “With TypeScript, it is possible to use existing JavaScript code, incorporate popular JavaScript libraries, and call TypeScript-generated code from other JavaScript.”

Proper Usage of .d.ts Files

Creating Declaration Files

To create a declaration file, you need to understand when and how to use them:

  1. For Third-Party Libraries: When using JavaScript libraries that don’t have built-in TypeScript support
  2. For Legacy JavaScript Code: When working with existing JavaScript code that needs type safety
  3. For Module Exports: When defining types for modules you’re writing in JavaScript

Basic Syntax

Declaration files follow TypeScript syntax but without implementation:

typescript
// Example declaration file (index.d.ts)
interface MyLibraryConfig {
  apiKey: string;
  timeout?: number;
}

function initialize(config: MyLibraryConfig): Promise<void>;
const version: string;

Referencing JavaScript Files

The TypeScript compiler automatically associates declaration files with JavaScript files through naming conventions:

  • myfile.jsmyfile.d.ts
  • myfile.tsmyfile.d.ts
  • index.jsindex.d.ts
  • index.tsindex.d.ts

The TypeScript compiler looks for declaration files with the same base name in the same directory or in @types packages.

Can .d.ts Files Replace .ts Files?

This is a common point of confusion. The answer is generally no, but with some important nuances:

When You CAN Remove .ts Files

You can remove a .ts file if:

  • You have a complete .d.ts file that defines all the types
  • The actual implementation is in a .js file
  • You’re not planning to modify the implementation in TypeScript

When You CANNOT Remove .ts Files

You should keep the .ts file when:

  • You’re actively developing and need to modify the implementation
  • The declaration file is incomplete or partial
  • You want to maintain type safety for your own code
  • You’re using TypeScript features that need to be compiled

The key insight is that .d.ts files provide type information but no execution logic. If you’re writing new code, you typically want to keep the .ts file to maintain the ability to modify and recompile.

How .d.ts Files Correspond to JavaScript Files

This is perhaps the most confusing aspect for TypeScript newcomers. The correspondence works through several mechanisms:

1. Automatic File Association

TypeScript automatically associates declaration files with JavaScript files based on naming:

typescript
// project structure
src/
  utils.js          // Implementation
  utils.d.ts        // Type definitions

The TypeScript compiler understands that utils.d.ts provides types for utils.js.

2. Triple-Slash Directives

For more complex scenarios, you can use triple-slash directives:

typescript
/// <reference types="node" />
/// <reference path="./types.d.ts" />

3. Package.json References

In npm packages, you can specify types in package.json:

json
{
  "name": "my-package",
  "types": "index.d.ts",
  "main": "index.js"
}

4. Module Resolution

TypeScript uses its module resolution system to find declaration files in node_modules/@types directories or relative to the JavaScript files.

The key principle is: declaration files describe what exists in JavaScript files, but they don’t include or merge with them.

Practical Examples and Best Practices

Example 1: Creating Types for a JavaScript Library

typescript
// @types/my-library/index.d.ts
declare module 'my-library' {
  export function doSomething(input: string): number;
  export const version: string;
  export interface Config {
    debug: boolean;
    port: number;
  }
  export function initialize(config: Config): Promise<void>;
}

Example 2: Augmenting Existing Types

typescript
// my-types.d.ts
interface Window {
  myCustomProperty: string;
  myCustomFunction(): void;
}

Example 3: Creating Declaration Files for Your Own Code

typescript
// src/utils.d.ts
export interface MathUtils {
  add(a: number, b: number): number;
  multiply(a: number, b: number): number;
}

// src/utils.js (implementation)
export const mathUtils = {
  add: (a, b) => a + b,
  multiply: (a, b) => a * b
};

Best Practices

  1. Keep declaration files separate from implementation files
  2. Use declare keyword for ambient declarations
  3. Include comprehensive type information to catch type errors
  4. Use export and import properly for module declarations
  5. Consider using DefinitelyTyped for popular libraries

Advanced Scenarios and Troubleshooting

Global vs. Module Declarations

  • Global declarations affect the entire TypeScript project
  • Module declarations are scoped to specific modules
typescript
// Global declaration
declare var myGlobalVar: string;

// Module declaration
declare module 'external-lib' {
  export function doWork(): void;
}

Type Augmentation

You can extend existing types:

typescript
// Extending built-in types
interface String {
  toCustomCase(): string;
}

// Extending library types
interface SomeLibrary {
  newFeature(): void;
}

Common Issues and Solutions

  1. “Could not find module” errors

    • Ensure declaration files are in the same directory
    • Check module configuration in tsconfig.json
  2. Type mismatch between declaration and implementation

    • Keep declaration files in sync with JavaScript changes
    • Use TypeScript compiler to verify consistency
  3. Declaration files not being recognized

    • Check file extensions and naming
    • Verify tsconfig.json configuration

Conclusion

TypeScript declaration files (.d.ts) are essential tools for providing type safety in JavaScript projects, but they work differently than C/C++ header files. Key takeaways:

  1. Declaration files contain only type information without implementation code
  2. They work alongside JavaScript files rather than being included in them
  3. You generally cannot replace .ts files with .d.ts files if you need to maintain and modify the implementation
  4. TypeScript automatically associates declaration files with JavaScript files through naming conventions and module resolution
  5. Declaration files are crucial for working with JavaScript libraries and legacy code in TypeScript projects

For developers transitioning from C/C++, understanding this fundamental difference is key to effectively leveraging TypeScript’s type system. The ecosystem provides powerful tools for maintaining type safety while working with existing JavaScript code.

Sources

  1. TypeScript - Wikipedia
  2. Why TypeScript’s “strict: true” isn’t enough | ITNEXT
  3. A Beginner-Friendly Guide to TypeScript (What I Wish I Knew Earlier) - DEV Community
  4. Mastering TypeScript with TSD: A Step-by-Step Guide - Krybot Blog
  5. Turborepo custom packages can’t import @prisma/client-runtime-utils due to pnpm package isolation · GitHub
Авторы
Проверено модерацией
Модерация