阿里云主机折上折
  • 微信号
Current Site:Index > Writing type declaration files

Writing type declaration files

Author:Chuan Chen 阅读数:61831人阅读 分类: TypeScript

The Role of Type Declaration Files

Type declaration files (.d.ts) provide type information for TypeScript, enabling developers to add static type checking to JavaScript libraries or modules. When using third-party JavaScript libraries, if there is no type declaration file, the TypeScript compiler will report errors. Declaration files do not contain concrete implementations but only type definitions.

// Assume there is a JavaScript library mathUtils.js  
function add(a, b) {  
  return a + b;  
}  

// Corresponding declaration file mathUtils.d.ts  
declare function add(a: number, b: number): number;  

Ways to Create Declaration Files

There are three main ways to create declaration files:

  1. Manual Writing: Suitable for small projects or custom modules.
  2. Automatic Generation Using Tools: For example, dts-gen can generate declaration files based on existing JavaScript code.
  3. Obtaining from DefinitelyTyped: Most popular libraries have community-maintained type declarations.

Example of manually creating a declaration file:

// myLib.d.ts  
declare namespace MyLib {  
  interface Config {  
    timeout: number;  
    retries: number;  
  }  
    
  function init(config: Config): void;  
  function fetchData(url: string): Promise<any>;  
}  

Module Declarations vs. Global Declarations

Depending on the usage scenario, declaration files can be categorized as module declarations or global declarations:

Module Declarations

Suitable for module systems like CommonJS, AMD, or ES modules:

// Module declaration  
declare module 'module-name' {  
  export interface Options {  
    debug?: boolean;  
  }  
    
  export function create(options?: Options): void;  
}  

Global Declarations

Required when a library is imported via <script> tags:

// Global variable declaration  
declare const VERSION: string;  

// Global function declaration  
declare function greet(name: string): void;  

// Global class declaration  
declare class Utils {  
  static formatDate(date: Date): string;  
}  

Type Merging and Extension

TypeScript supports extending existing types through declaration merging:

Interface Merging

// Original declaration  
interface User {  
  name: string;  
}  

// Extended declaration  
interface User {  
  age: number;  
}  

// After merging, equivalent to:  
interface User {  
  name: string;  
  age: number;  
}  

Namespace Merging

// Original namespace  
namespace API {  
  export function get(url: string): Promise<any>;  
}  

// Extended namespace  
namespace API {  
  export function post(url: string, data: any): Promise<any>;  
}  

Common Type Declaration Patterns

Function Overload Declarations

declare function createElement(tag: 'div'): HTMLDivElement;  
declare function createElement(tag: 'span'): HTMLSpanElement;  
declare function createElement(tag: string): HTMLElement;  

Class Declarations

declare class Animal {  
  constructor(name: string);  
  name: string;  
  move(distance: number): void;  
}  

Generic Types

declare interface Response<T = any> {  
  data: T;  
  status: number;  
}  

declare function fetch<T>(url: string): Promise<Response<T>>;  

Handling Complex Types

Conditional Types

declare type NonNullable<T> = T extends null | undefined ? never : T;  

declare type Flatten<T> = T extends Array<infer U> ? U : T;  

Mapped Types

declare type Readonly<T> = {  
  readonly [P in keyof T]: T[P];  
};  

declare type Partial<T> = {  
  [P in keyof T]?: T[P];  
};  

Advanced Features in Declaration Files

Triple-Slash Directives

Used to declare dependencies between files:

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

Global Augmentation

Extend the global scope within a module using declare global:

// In a module declaration file  
export {};  

declare global {  
  interface Window {  
    myCustomProp: string;  
  }  
}  

Handling Special Cases in Third-Party Libraries

Module Augmentation

When extending third-party modules:

import { OriginalModule } from 'some-module';  

declare module 'some-module' {  
  interface OriginalModule {  
    newMethod(): void;  
  }  
}  

Default Export Handling

// For CommonJS modules  
declare module 'module-name' {  
  const value: any;  
  export = value;  
}  

// For ES module default exports  
declare module 'es-module' {  
  const defaultValue: any;  
  export default defaultValue;  
}  

Best Practices for Type Declarations

  1. Prefer interfaces over type aliases unless union types or tuples are needed.
  2. Provide complete type definitions for public APIs.
  3. Use JSDoc comments to enhance type declarations.
  4. Keep declaration files synchronized with implementations.
  5. Include examples for complex types.
/**
 * Calculates the sum of two numbers  
 * @param a - The first addend  
 * @param b - The second addend  
 * @returns The sum of the two numbers  
 */  
declare function add(a: number, b: number): number;  

Testing Type Declaration Files

Tools like tsd can be used to test type declarations:

import { expectType } from 'tsd';  

expectType<string>(add(1, 2)); // This will error because add returns a number  

Publishing Type Declaration Files

When publishing a package with type declarations, there are two approaches:

  1. Include .d.ts files directly in the package and specify the types field in package.json.
  2. Publish to DefinitelyTyped to provide declarations via the @types scope.

Example package.json configuration:

{  
  "name": "my-package",  
  "version": "1.0.0",  
  "types": "./dist/index.d.ts",  
  "files": ["dist"]  
}  

Handling Libraries Without Type Declarations

For libraries without type declarations, create a types directory and configure tsconfig.json:

{  
  "compilerOptions": {  
    "typeRoots": ["./node_modules/@types", "./types"]  
  }  
}  

Then create the corresponding declaration file in the types directory:

types/  
  some-library/  
    index.d.ts  

Common Issues in Type Declarations

Circular Type References

Solution:

// a.d.ts  
import type { B } from './b';  

export interface A {  
  b: B;  
}  

// b.d.ts  
import type { A } from './a';  

export interface B {  
  a?: A;  
}  

Dynamic Property Access

declare interface DynamicObject {  
  [key: string]: any;  
}  

// More precise typing  
declare interface StringMap<T> {  
  [key: string]: T;  
}  

Performance Optimization in Type Declarations

Excessive type declarations may impact compilation performance. Optimize by:

  1. Using import type to avoid importing actual code.
  2. Splitting large declarations into multiple files.
  3. Avoiding overuse of conditional types and complex type operations.
// Use type-only imports  
import type { SomeType } from 'some-module';  

// Instead of  
import { SomeType } from 'some-module';  

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.