Type declaration file (.d.ts)
Basic Concepts of Type Declaration Files (.d.ts)
Type declaration files are special files in TypeScript used to describe the types of JavaScript libraries or modules. They have the .d.ts
extension, contain no actual implementation code, and only include type information. When using libraries written in pure JavaScript, .d.ts
files provide TypeScript with the necessary type-checking support.
// example.d.ts
declare module "some-library" {
export function doSomething(value: string): number;
export interface Options {
timeout?: number;
retries?: number;
}
}
Why Type Declaration Files Are Needed
The JavaScript ecosystem has a vast number of libraries not written in TypeScript. Type declaration files allow these libraries to benefit from type checking when used in TypeScript projects. They act as a bridge between JavaScript code and TypeScript's type system, offering the following advantages:
- Code completion and intelligent hints
- Compile-time type checking
- Better documentation support
- Refactoring safety
Three Sources of Type Declaration Files
Built-in Type Declarations
TypeScript comes with type declarations for certain libraries, such as DOM APIs and ECMAScript built-in objects. These declarations are included in the TypeScript installation package.
// Built-in lib.dom.d.ts contains DOM types
document.getElementById("app"); // Automatically provides type hints
Third-Party Type Declarations
Provided by the DefinitelyTyped project via @types
packages. When installing a package like @types/react
, you are essentially installing the type declaration files for the React library.
npm install --save-dev @types/lodash
Custom Type Declarations
Developers can create .d.ts
files for their own JavaScript code or for third-party libraries lacking type declarations.
// custom.d.ts
declare module "my-untyped-module" {
export const version: string;
export function calculate(a: number, b: number): number;
}
Syntax of Type Declaration Files
Declaring Variables
declare const PI: number;
declare let config: {
apiUrl: string;
debug: boolean;
};
Declaring Functions
declare function greet(name: string): string;
declare function fetchData(url: string, options?: { timeout?: number }): Promise<any>;
Declaring Classes and Interfaces
declare class User {
constructor(name: string);
id: number;
getName(): string;
}
declare interface Point {
x: number;
y: number;
distanceTo(other: Point): number;
}
Declaring Modules
declare module "*.png" {
const value: string;
export default value;
}
declare module "jquery" {
interface JQuery {
modal(options?: any): JQuery;
}
function $(ready: () => void): void;
export = $;
}
Global Declarations vs. Module Declarations
Global Declarations
Global declarations are available in any file without needing to import them. They are typically used to extend global objects or declare global variables.
// global.d.ts
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production';
API_KEY: string;
}
}
Module Declarations
Module declarations require import statements and are used to describe the types of specific modules.
// module.d.ts
declare module "my-module" {
export function init(config: { debug: boolean }): void;
export class Logger {
log(message: string): void;
}
}
Type Merging and Augmentation
TypeScript allows merging type declarations with the same name, which is useful for extending existing types.
Interface Merging
// original.d.ts
interface User {
name: string;
}
// extension.d.ts
interface User {
age: number;
}
// Result after merging:
// interface User {
// name: string;
// age: number;
// }
Namespace Merging
// original.d.ts
namespace Utils {
export function formatDate(date: Date): string;
}
// extension.d.ts
namespace Utils {
export function formatCurrency(amount: number): string;
}
Conditional Types and Advanced Types
Advanced TypeScript features can also be used in .d.ts
files.
declare type Nullable<T> = T | null;
declare type Record<K extends keyof any, T> = {
[P in K]: T;
};
declare function getProperty<T, K extends keyof T>(obj: T, key: K): T[K];
Common Issues and Solutions
Handling Untyped Modules
When using modules without type declarations, you can quickly declare them as any
:
declare module "untyped-module";
Extending Third-Party Types
import { OriginalType } from "some-library";
declare module "some-library" {
interface OriginalType {
newMethod(): void;
}
}
Avoiding Global Pollution
Use module declarations to avoid polluting the global scope:
// Not recommended
declare const myGlobal: string;
// Recommended
declare module "my-globals" {
export const myGlobal: string;
}
Testing Type Declaration Files
After creating .d.ts
files, validate their correctness:
- Create test files to import the declarations
- Check if type inference is correct
- Verify edge cases
// test.ts
import { calculate } from "my-untyped-module";
const result = calculate(1, 2); // Should correctly infer as number type
Publishing Type Declaration Files
If you create type declarations for a library, publish them in the following ways:
- Include them with the library code (using the
types
field) - Publish to DefinitelyTyped
- Release as a separate
@types
package
// package.json
{
"name": "my-library",
"types": "dist/index.d.ts",
"files": ["dist"]
}
Best Practices
- Keep declaration files synchronized with implementations
- Use precise types instead of
any
- Provide documentation comments for complex types
- Organize related declarations into namespaces
- Prefer interfaces over type aliases
- Consider backward compatibility
/**
* Basic user information
*/
interface User {
/**
* Unique user identifier
*/
id: number;
/**
* User display name
*/
name: string;
}
Tools and Ecosystem
dts-gen
A tool for automatically generating type declaration files:
npx dts-gen -m <module-name>
TypeScript Compiler Options
Relevant configurations:
{
"compilerOptions": {
"typeRoots": ["./typings", "./node_modules/@types"],
"types": ["node", "lodash"],
"declaration": true
}
}
Complex Scenario Examples
Declaring React Higher-Order Components
import * as React from 'react';
declare module 'react' {
function memo<T extends React.ComponentType<any>>(
Component: T,
propsAreEqual?: (prevProps: React.ComponentProps<T>, nextProps: React.ComponentProps<T>) => boolean
): T;
}
Declaring Vue Plugins
import { PluginObject } from 'vue';
declare module 'vue/types/vue' {
interface Vue {
$myPlugin: {
showToast(message: string): void;
};
}
}
declare const MyPlugin: PluginObject<{ options: any }>;
export default MyPlugin;
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn