Category: reference
TypeScript Module Augmentation & Declaration Merging
เพิ่ม types ให้ third-party modules, extend interface ที่มีอยู่, และสร้าง .d.ts files สำหรับ JavaScript libraries
สารบัญ
Declaration Merging คืออะไร
TypeScript อนุญาตให้ประกาศ interface หรือ namespace เดียวกันหลายครั้ง แล้วจะ merge เข้าด้วยกันอัตโนมัติ — ทำให้เราเพิ่ม properties ให้ existing types ได้
// ✓ Interface merging — ประกาศ interface ชื่อเดียวกัน 2 ครั้ง
interface User {
id: number;
name: string;
}
interface User {
email: string; // เพิ่ม property ใหม่
}
// ผลลัพธ์: User มีทั้ง id, name, email
const user: User = { id: 1, name: 'Alice', email: 'alice@example.com' };
Module Augmentation — เพิ่ม Types ให้ Library
// เพิ่ม method ให้ Express Request object
// src/types/express.d.ts
import 'express'; // ต้อง import ก่อนเสมอ (ทำให้ file เป็น module)
declare module 'express' {
interface Request {
user?: {
id: string;
email: string;
role: 'admin' | 'user';
};
requestId?: string;
}
}
// ตอนใช้งาน — TypeScript รู้จัก req.user แล้ว
app.get('/profile', (req, res) => {
if (!req.user) return res.status(401).send('Unauthorized');
res.json({ email: req.user.email }); // ✓ type-safe
});
Augment Node.js ProcessEnv
// src/types/env.d.ts
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
DATABASE_URL: string;
PORT?: string;
JWT_SECRET: string;
}
}
// ตอนใช้งาน
process.env.NODE_ENV // 'development' | 'production' | 'test'
process.env.DATABASE_URL // string (ไม่ใช่ string | undefined)
process.env.PORT // string | undefined (optional)
Augment Window Object
// src/types/window.d.ts
export {}; // ทำให้ file เป็น module
declare global {
interface Window {
analytics: {
track(event: string, properties?: Record<string, unknown>): void;
};
__APP_VERSION__: string;
}
}
// ใช้งาน
window.analytics.track('page_view', { path: '/about' });
window.__APP_VERSION__ // string
สร้าง .d.ts สำหรับ JavaScript Library ที่ไม่มี Types
// types/my-legacy-lib.d.ts
declare module 'my-legacy-lib' {
export interface Options {
timeout?: number;
retries?: number;
}
export function connect(url: string, options?: Options): Promise<void>;
export function disconnect(): void;
export function query<T>(sql: string, params?: unknown[]): Promise<T[]>;
// Default export
const client: {
connect: typeof connect;
disconnect: typeof disconnect;
query: typeof query;
};
export default client;
}
Ambient Declarations — declare ไม่ได้ implement
// ประกาศ variable ที่มีอยู่ใน global scope (inject โดย bundler หรือ server)
declare const __DEV__: boolean;
declare const __VERSION__: string;
declare const __BUILD_TIME__: number;
// ใช้งาน
if (__DEV__) {
console.log('Development mode');
}
// .d.ts สำหรับ CSS modules
declare module '*.module.css' {
const classes: Record<string, string>;
export default classes;
}
// .d.ts สำหรับ SVG imports (Vite)
declare module '*.svg' {
const content: string;
export default content;
}
// .d.ts สำหรับ image imports
declare module '*.png' {
const url: string;
export default url;
}
Namespace Merging
// Original function + namespace ชื่อเดียวกัน
function createStore<T>(initial: T) {
return { state: initial };
}
// Merge namespace เข้ากับ function
namespace createStore {
export interface Options {
persist?: boolean;
key?: string;
}
export function fromJSON<T>(json: string): ReturnType<typeof createStore<T>> {
return createStore(JSON.parse(json) as T);
}
}
// ใช้งาน
const store = createStore({ count: 0 });
const opts: createStore.Options = { persist: true };
const restored = createStore.fromJSON<{ count: number }>('{"count":5}');
tsconfig สำหรับ .d.ts files
// tsconfig.json
{
"compilerOptions": {
"typeRoots": ["./node_modules/@types", "./src/types"],
// เพิ่ม src/types/ ให้ TypeScript หา .d.ts อัตโนมัติ
"paths": {
// ถ้าใช้ path aliases
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts"]
}
กฎสำคัญ
// ✓ file ที่มี import/export = module
// ต้อง declare global { ... } เมื่อต้องการ extend global
import 'some-module';
declare global {
interface Window { myProp: string; }
}
// ✓ file ที่ไม่มี import/export = script (global scope)
// extend global ได้โดยตรง
interface Window { myProp: string; }
// ❌ ถ้าไม่ import module ก่อน — augmentation ไม่ทำงาน
declare module 'express' { /* ... */ } // ไม่มี import 'express' → ไม่ merge
// ✓ ถูกต้อง
import 'express';
declare module 'express' {
interface Request { user?: User; }
}
ตรวจสอบ Types ที่มีอยู่
# ดู types ที่ติดตั้งแล้ว
ls node_modules/@types/
# ติดตั้ง type definitions
npm install --save-dev @types/node @types/express
# ดู .d.ts ที่ library export (ถ้ามี built-in types)
cat node_modules/zod/lib/index.d.ts | head -30