Module expansion
Basic Concepts of Module Augmentation
TypeScript's module augmentation allows developers to extend existing modules without modifying their original declarations. This mechanism is particularly suitable for adding type definitions to third-party libraries or enhancing built-in types. The core idea of module augmentation is to leverage declaration merging through the declare module
syntax to extend modules at the type level.
// Original module declaration
declare module 'original-module' {
export interface Config {
timeout: number;
}
}
// Module augmentation
declare module 'original-module' {
export interface Config {
retries?: number; // Added optional property
}
export function newMethod(): void; // Added method
}
Methods for Augmenting Global Modules
For types in the global scope, augmentation can be done directly through declaration merging. Common scenarios include extending browser built-in objects or Node.js global variables.
// Extending the Window interface
interface Window {
myCustomProp: string;
}
// Extending the Array prototype
interface Array<T> {
shuffle(): T[];
}
Array.prototype.shuffle = function() {
// Implementation code
return this.sort(() => Math.random() - 0.5);
};
Type Augmentation for Third-Party Libraries
When using third-party libraries with incomplete type definitions, module augmentation perfectly solves type-related issues. Taking the popular lodash library as an example:
// Lodash module augmentation
declare module 'lodash' {
interface LoDashStatic {
multiplyByTwo(value: number): number;
deepClone<T>(obj: T): T;
}
}
// Actual implementation needs to be provided separately
_.multiplyByTwo = (n) => n * 2;
_.deepClone = (obj) => JSON.parse(JSON.stringify(obj));
Namespace Merging Techniques
Module augmentation can be combined with namespace merging to create more complex type structures:
// Original namespace
namespace Utilities {
export function log(message: string) {
console.log(message);
}
}
// Augmenting the namespace
namespace Utilities {
export function error(message: string) {
console.error(message);
}
export namespace Math {
export function square(x: number) {
return x * x;
}
}
}
Special Handling for Ambient Modules
Modules without default exports require special handling during augmentation:
// Assuming a CSS module declaration
declare module '*.css' {
const classes: { readonly [key: string]: string };
export default classes;
}
// Augmenting CSS modules to support SCSS
declare module '*.scss' {
const classes: { readonly [key: string]: string };
export default classes;
}
Module Augmentation with Type Guards
Type guards can be combined with module augmentation to enhance type safety:
declare module 'my-library' {
interface User {
id: string;
name: string;
}
export function isUser(obj: any): obj is User;
}
// Usage example
import { isUser } from 'my-library';
const data = JSON.parse(response);
if (isUser(data)) {
console.log(data.name); // Type-safe access
}
Generic Module Augmentation Patterns
For scenarios requiring type flexibility, generics can be used for module augmentation:
declare module 'generic-module' {
export interface ResponseWrapper<T = any> {
data: T;
status: number;
meta?: {
timestamp: Date;
version: string;
};
}
export function wrapResponse<T>(data: T): ResponseWrapper<T>;
}
Cross-Module Joint Augmentation
When types need to be augmented across modules, joint declarations can be used:
// Cross-module type references
declare module 'module-a' {
export interface SharedType {
id: string;
}
}
declare module 'module-b' {
import { SharedType } from 'module-a';
export function process(input: SharedType): void;
}
Type Augmentation for Dynamic Module Loading
Type support for dynamic import scenarios:
declare module 'dynamic-module' {
export type ModuleLoader<T> = () => Promise<{ default: T }>;
export function loadComponent<T>(
loader: ModuleLoader<T>,
fallback?: React.ReactNode
): React.ComponentType<T>;
}
Best Practices for Module Augmentation
- Maintain consistency between augmentation declarations and original declarations
- Add clear JSDoc comments for each augmentation
- Place augmentations in a centralized
types
folder at the project root - Avoid circular dependencies in augmentation declarations
- Prefer interface extension over type aliases
/**
* Extending React's default attributes
*/
declare module 'react' {
interface HTMLAttributes<T> {
// Adding custom data attributes
'data-custom'?: string;
// Supporting extended attributes for micro-frontend scenarios
'data-module'?: string;
}
}
Solutions to Common Problems
Handling naming conflicts in module augmentation:
// Using namespaces to isolate conflicts
declare module 'conflict-module' {
namespace Extended {
export interface Options {
customOption: boolean;
}
}
export function method(options: Extended.Options): void;
}
Handling type overwriting issues:
// Using the Omit utility type to avoid property overwriting
declare module 'overwrite-module' {
interface Original {
prop: string;
}
interface Extended extends Omit<Original, 'prop'> {
prop: number; // Safe overwrite
newProp: boolean;
}
}
Advanced Patterns: Conditional Type Augmentation
Implementing intelligent augmentation with TypeScript's conditional types:
declare module 'conditional-module' {
export type SmartResult<T> =
T extends string ? { text: T } :
T extends number ? { value: T } :
{ data: T };
export function smartProcess<T>(input: T): SmartResult<T>;
}
Performance Optimization Considerations
Optimization strategies for module augmentation in large projects:
// Using references to reduce duplicate declarations
/// <reference types="./extensions/date" />
/// <reference types="./extensions/array" />
// On-demand type augmentation
declare module 'large-library' {
export type FeatureFlags =
| 'newDashboard'
| 'experimentalAPI'
| 'legacySupport';
export function isFeatureEnabled(flag: FeatureFlags): boolean;
}
Testing Strategy Validation
Testing methods to ensure the correctness of module augmentation:
// Type testing example
import { expectType } from 'tsd';
declare module 'tested-module' {
export function addedMethod(input: string): number;
}
expectType<number>(addedMethod('test'));
Toolchain Integration
Configuration for working with build tools:
// Webpack environment variable augmentation
declare module 'process' {
global {
namespace NodeJS {
interface ProcessEnv {
readonly CUSTOM_ENV: 'development' | 'production' | 'test';
readonly ANALYTICS_ID?: string;
}
}
}
}
Historical Version Compatibility
Handling type augmentation differences across library versions:
// Version-aware type augmentation
declare module 'versioned-library' {
export interface ApiV1 {
oldMethod(): void;
}
export interface ApiV2 {
newMethod(): void;
}
export function getApi(version: 1): ApiV1;
export function getApi(version: 2): ApiV2;
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn