// math.ts - named exportsexport const PI = 3.14159;export const E = 2.71828;export function add(a: number, b: number): number { return a + b;}export function multiply(a: number, b: number): number { return a * b;}export interface MathResult { value: number; operation: string;}// main.ts - named importsimport { PI, E, add, multiply } from './math';console.log(PI, E);const sum = add(5, 3);const product = multiply(4, 2);// Import with aliasimport { add as sum, multiply as times } from './math';// Import specific itemsimport { PI } from './math';// Import type onlyimport type { MathResult } from './math';
Example: Default exports and mixed exports
// user.ts - default exportexport default class User { constructor(public name: string) {} greet() { return `Hello, ${this.name}`; }}// Alternative: export after declarationclass User { constructor(public name: string) {}}export default User;// app.ts - import defaultimport User from './user';const user = new User('Alice');// mixed.ts - both default and named exportsexport default class Database { connect() { /* ... */ }}export const DB_VERSION = '1.0';export function createConnection() { /* ... */ }// Import bothimport Database, { DB_VERSION, createConnection } from './mixed';// Can rename default importimport DB from './mixed';
Example: Re-exports and barrel exports
// models/user.tsexport interface User { id: string; name: string;}// models/product.tsexport interface Product { id: string; price: number;}// models/index.ts - barrel exportexport { User } from './user';export { Product } from './product';export * from './order'; // Re-export all// Or rename during re-exportexport { User as UserModel } from './user';// app.ts - import from barrelimport { User, Product } from './models';// Re-export with modificationsexport { User } from './user';export type { Product } from './product'; // Type-only re-exportexport { Order as PurchaseOrder } from './order';
Example: Namespace imports and dynamic imports
// utils.tsexport const version = '1.0';export function format(s: string) { return s.toUpperCase(); }export function parse(s: string) { return s.toLowerCase(); }// Import entire module as namespaceimport * as Utils from './utils';console.log(Utils.version);const formatted = Utils.format('hello');// Dynamic import (returns Promise)async function loadModule() { const utils = await import('./utils'); console.log(utils.version); utils.format('test');}// Conditional loadingif (condition) { import('./feature').then(module => { module.initialize(); });}// Dynamic import with typetype UtilsModule = typeof import('./utils');const module: UtilsModule = await import('./utils');
// Without esModuleInterop (old style)import * as express from 'express';const app = express(); // Works// With esModuleInterop: true (recommended)import express from 'express';const app = express(); // Works like CommonJS// require() in TypeScript (not recommended)const fs = require('fs'); // Type: anyconst fs: typeof import('fs') = require('fs'); // With types// Import assignment (legacy)import fs = require('fs');fs.readFileSync('/path');// Type-only import for CommonJS typesimport type { Express } from 'express';const app: Express = createExpressApp();
Example: TypeScript compiled to CommonJS
// source.ts - TypeScript sourceexport const value = 42;export default class MyClass {}// Compiled to CommonJS (target: "commonjs")"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.value = void 0;exports.value = 42;class MyClass {}exports.default = MyClass;// __esModule flag helps with interop// TypeScript/Babel use it to detect ES modules// Import in Node.jsconst mod = require('./source');console.log(mod.value); // 42console.log(mod.default); // MyClass (with esModuleInterop)// With esModuleInterop, can use:const MyClass = require('./source').default;
Note: Enable esModuleInterop and allowSyntheticDefaultImports in
tsconfig.json for better CommonJS compatibility. This allows import React from 'react' instead of
import * as React from 'react'.
3. Module Resolution Strategies (Node, Classic)
Strategy
Algorithm
Use Case
File Extensions
Node (Node10)
Mimics Node.js require() resolution
Most common, npm packages
.ts, .tsx, .d.ts, .js, .jsx
Node16/NodeNext
Node.js ESM resolution with package.json
Modern Node.js ESM projects
Respects "type": "module"
Classic
Legacy pre-1.6 resolution
Rarely used, backwards compat
.ts, .d.ts only
Bundler
Bundler-like resolution (Webpack/Vite)
SPA applications with bundlers
Assumes bundler handles resolution
Example: Node resolution strategy
// For: import { x } from './module';// TypeScript searches in order:// 1. Relative import './module'./module.ts./module.tsx./module.d.ts./module/package.json (check "types" field)./module/index.ts./module/index.tsx./module/index.d.ts// 2. Non-relative import 'lodash'// From /root/src/app.ts:/root/src/node_modules/lodash.ts/root/src/node_modules/lodash.tsx/root/src/node_modules/lodash.d.ts/root/src/node_modules/lodash/package.json ("types" field)/root/src/node_modules/lodash/index.ts/root/src/node_modules/lodash/index.d.ts/root/node_modules/lodash/.../node_modules/lodash/...// 3. Check @types/root/src/node_modules/@types/lodash.d.ts/root/src/node_modules/@types/lodash/index.d.ts/root/node_modules/@types/lodash/.../node_modules/@types/lodash/...
Example: Node16/NodeNext resolution
// package.json configuration{ "type": "module", // Use ESM "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.cjs" }, "./utils": { "types": "./dist/utils.d.ts", "import": "./dist/utils.js" } }}// With Node16/NodeNext, must use extensions// ✗ Error - missing extensionimport { x } from './module';// ✓ Correct - with extensionimport { x } from './module.js'; // .js even for .ts files!// For directories, must use indeximport { y } from './utils/index.js';// Package importsimport pkg from 'package'; // Uses package.json "exports"import sub from 'package/submodule'; // Must be in exports map
Example: Path mapping and baseUrl
// tsconfig.json{ "compilerOptions": { "baseUrl": "./src", "paths": { "@/*": ["./*"], "@components/*": ["components/*"], "@utils/*": ["utils/*"], "@models": ["models/index"] } }}// Usage with path mappingimport { Button } from '@components/Button';import { User } from '@models';import { format } from '@utils/string';import config from '@/config';// Without mapping (relative paths)import { Button } from '../../components/Button';import { User } from '../../models/index';import { format } from '../utils/string';import config from './config';// Note: Bundlers need to be configured to understand paths// Webpack: use tsconfig-paths-webpack-plugin// Vite: use resolve.alias
Warning: Path mapping in tsconfig.json is compile-time only. For Node.js runtime, use a tool
like tsconfig-paths or configure your bundler. Node16/NodeNext resolution doesn't support paths
mapping.
4. Declaration Files (.d.ts) and Type Declarations
Feature
Syntax
Purpose
Use Case
Declaration File
.d.ts
Type definitions without implementation
Type libraries, JS libraries
declare var/let/const
declare const x: T;
Declare global variable
Global variables from scripts
declare function
declare function fn(): T;
Declare global function
Global functions
declare class
declare class C {}
Declare class structure
Classes from JS
declare namespace
declare namespace N {}
Group related declarations
Library namespaces
declare module
declare module 'pkg' {}
Module augmentation
Add types to external modules
Example: Basic declaration file
// types.d.ts - declaration file// Declare global variabledeclare const VERSION: string;declare let currentUser: User | null;// Declare global functiondeclare function $(selector: string): Element;declare function fetch(url: string): Promise<Response>;// Declare classdeclare class EventEmitter { on(event: string, handler: Function): void; emit(event: string, ...args: any[]): void;}// Declare interface (no declare needed)interface User { id: string; name: string;}// Declare type aliastype ID = string | number;// Declare enumdeclare enum Color { Red, Green, Blue}// Usage in .ts files (no import needed for global declarations)console.log(VERSION);const el = $('div');const user: User = { id: '1', name: 'Alice' };
// tsconfig.json{ "compilerOptions": { "declaration": true, // Generate .d.ts files "declarationMap": true, // Generate .d.ts.map for navigation "emitDeclarationOnly": false, // Emit both .js and .d.ts "declarationDir": "./types" // Output directory for .d.ts }}// source.tsexport interface User { id: string; name: string;}export function createUser(name: string): User { return { id: Math.random().toString(), name };}export default class UserManager { private users: User[] = []; add(user: User): void { this.users.push(user); }}// Generated: source.d.tsexport interface User { id: string; name: string;}export declare function createUser(name: string): User;export default class UserManager { private users; add(user: User): void;}
Example: @types packages
// Install type definitions// npm install --save-dev @types/node// npm install --save-dev @types/express// npm install --save-dev @types/react// @types/node provides Node.js typesimport * as fs from 'fs';import * as path from 'path';fs.readFileSync('./file.txt'); // Types available// @types/express provides Express typesimport express from 'express';const app = express();// Some packages include their own types// package.json has "types" fieldimport lodash from 'lodash-es'; // Has built-in types// Check if types exist: https://www.npmjs.com/~types// Or search: npm search @types/package-name// Override @types with local declarations// Create: node_modules/@types/custom/index.d.ts// Or configure typeRoots in tsconfig.json{ "compilerOptions": { "typeRoots": ["./types", "./node_modules/@types"] }}
5. Ambient Module Declarations
Pattern
Syntax
Purpose
Scope
Ambient Module
declare module 'name' {}
Provide types for untyped modules
Project-wide
Wildcard Module
declare module '*.css' {}
Type non-JS imports (CSS, images, etc.)
Asset imports
Module Augmentation
declare module 'pkg' { export ... }
Add types to existing module
Extend libraries
Global Augmentation
declare global {}
Add to global scope from module
Global extensions
Example: Ambient module declarations
// types.d.ts - ambient declarations// Declare module with no types availabledeclare module 'legacy-lib' { export function doSomething(value: any): any; export const VERSION: string;}// Now can importimport { doSomething, VERSION } from 'legacy-lib';// Minimal typing for quick fixesdeclare module 'no-types-available' { const content: any; export default content;}import lib from 'no-types-available';// Specific module path patterndeclare module '@company/*/config' { interface Config { apiKey: string; endpoint: string; } const config: Config; export default config;}import config from '@company/users/config';import config2 from '@company/products/config';
// Extend existing module with new exportsdeclare module 'express' { // Add custom properties to Request interface Request { user?: { id: string; name: string; }; startTime?: number; } // Add custom response methods interface Response { sendSuccess(data: any): void; sendError(message: string): void; }}// Now can use augmented typesimport { Request, Response } from 'express';app.use((req: Request, res: Response, next) => { req.startTime = Date.now(); // ✓ Type-safe if (req.user) { console.log(req.user.name); // ✓ Type-safe } res.sendSuccess({ ok: true }); // ✓ Type-safe});// Augment module from within a module// my-plugin.tsimport 'express';declare module 'express' { interface Request { customField: string; }}// Available after importimport './my-plugin';app.get('/', (req, res) => { console.log(req.customField); // ✓ Available});
Example: Global augmentation from module
// extensions.d.ts - module file with global augmentation// Must have at least one import/export to be a moduleexport {};// Augment global scopedeclare global { // Add to Window interface interface Window { myApp: { version: string; config: AppConfig; }; gtag: (command: string, ...args: any[]) => void; } // Add global variable var APP_ENV: 'development' | 'production'; // Add to Array prototype interface Array<T> { firstOrNull(): T | null; lastOrNull(): T | null; } // Add to String prototype interface String { toKebabCase(): string; toCamelCase(): string; }}// Usage anywhere in projectwindow.myApp.version; // ✓ Type-safeconsole.log(APP_ENV); // ✓ Type-safeconst arr = [1, 2, 3];const first = arr.firstOrNull(); // ✓ Type-safeconst str = 'hello-world';const kebab = str.toKebabCase(); // ✓ Type-safe
6. Triple-Slash Directives and Reference Types
Directive
Syntax
Purpose
Modern Alternative
/// <reference path="" />
Reference file by path
Include declaration file
Use import statements
/// <reference types="" />
Reference @types package
Include type package
Use types array in tsconfig
/// <reference lib="" />
Reference built-in lib
Include specific lib types
Use lib array in tsconfig
/// <reference no-default-lib="true" />
Exclude default lib
Custom lib definitions
Rarely needed
Example: Triple-slash path references (legacy)
// types.d.tsinterface User { id: string; name: string;}// main.ts - reference other file/// <reference path="./types.d.ts" />const user: User = { id: '1', name: 'Alice' };// Better modern approach: use importimport type { User } from './types';// Multiple references/// <reference path="./globals.d.ts" />/// <reference path="./utils.d.ts" />/// <reference path="./models.d.ts" />// Note: Triple-slash directives must be at top of file// Before any code or imports
Example: Reference types directive
// Include types from @types package/// <reference types="node" />// Now have Node.js types without explicit importconst fs: typeof import('fs') = require('fs');console.log(__dirname); // Global Node.js variable// Include jQuery types/// <reference types="jquery" />$('div').addClass('active'); // jQuery available globally// In declaration files (.d.ts)/// <reference types="react" />declare module 'my-react-library' { import { ComponentType } from 'react'; export const MyComponent: ComponentType<{}>;}// Modern approach in tsconfig.json{ "compilerOptions": { "types": ["node", "jest", "jquery"] }}