Prerequisites
- Understanding of TypeScript compilation process and project structure π
- Knowledge of module systems and file dependencies β‘
- Familiarity with build tools and configuration management π»
What you'll learn
- Master all triple-slash directive types and their applications π―
- Control compilation order and file dependencies effectively ποΈ
- Create sophisticated build configurations and reference systems π
- Build maintainable large-scale TypeScript projects β¨
π― Introduction
Welcome to the command center of TypeScriptβs compilation orchestra! π If TypeScript compilation were like conducting a symphony orchestra, then triple-slash directives are like the conductorβs precise gestures that tell each musician (file) exactly when to come in, which other musicians they need to listen to, and how they should coordinate to create beautiful, harmonious code together!
Triple-slash directives are special comments that provide instructions to the TypeScript compiler about file dependencies, compilation order, library references, and build configurations. Theyβre particularly powerful for managing complex projects, creating library definition files, and controlling the compilation process in sophisticated ways.
By the end of this tutorial, youβll be a master conductor of TypeScript compilation, capable of orchestrating complex build processes and creating maintainable, well-organized TypeScript projects that compile efficiently and correctly every time. Letβs master the art of compilation coordination! π
π Understanding Triple-Slash Directives
π€ What Are Triple-Slash Directives?
Triple-slash directives are single-line comments that start with ///
and contain XML-like tags that provide instructions to the TypeScript compiler. They must appear at the top of files (before any code) and are used to declare dependencies between files, reference external libraries, and control compilation behavior.
// π Basic triple-slash directive examples
/// <reference path="./utilities.ts" />
/// <reference types="node" />
/// <reference lib="es2017" />
// Now the file can use utilities and Node.js types
import { formatDate, validateEmail } from './utilities';
import * as fs from 'fs';
interface UserData {
id: string;
email: string;
createdAt: Date;
preferences: UserPreferences;
}
interface UserPreferences {
theme: 'light' | 'dark';
language: string;
notifications: NotificationSettings;
}
interface NotificationSettings {
email: boolean;
push: boolean;
frequency: 'immediate' | 'daily' | 'weekly';
}
class UserManager {
private users: Map<string, UserData> = new Map();
private dataFile: string = './users.json';
constructor() {
this.loadUsers();
}
// π― Using referenced utilities
createUser(email: string): UserData {
if (!validateEmail(email)) {
throw new Error('Invalid email format');
}
const userData: UserData = {
id: this.generateId(),
email,
createdAt: new Date(),
preferences: {
theme: 'light',
language: 'en',
notifications: {
email: true,
push: false,
frequency: 'daily'
}
}
};
this.users.set(userData.id, userData);
this.saveUsers();
return userData;
}
// π Using Node.js APIs (from types reference)
private loadUsers(): void {
try {
if (fs.existsSync(this.dataFile)) {
const data = fs.readFileSync(this.dataFile, 'utf8');
const usersArray: UserData[] = JSON.parse(data);
for (const user of usersArray) {
this.users.set(user.id, user);
}
}
} catch (error) {
console.error('Failed to load users:', error);
}
}
private saveUsers(): void {
try {
const usersArray = Array.from(this.users.values());
const data = JSON.stringify(usersArray, null, 2);
fs.writeFileSync(this.dataFile, data, 'utf8');
} catch (error) {
console.error('Failed to save users:', error);
}
}
// β¨ Using ES2017 features (from lib reference)
async processUsers(): Promise<void> {
const userEntries = Object.entries(this.users);
// Using ES2017 Object.entries and async/await
for (const [id, user] of userEntries) {
await this.processUserData(user);
}
}
private async processUserData(user: UserData): Promise<void> {
// Simulate async processing
return new Promise(resolve => {
setTimeout(() => {
console.log(`Processed user: ${user.email} (${formatDate(user.createdAt)})`);
resolve();
}, 100);
});
}
private generateId(): string {
return Math.random().toString(36).substr(2, 9);
}
getUserById(id: string): UserData | undefined {
return this.users.get(id);
}
getAllUsers(): UserData[] {
return Array.from(this.users.values());
}
updateUser(id: string, updates: Partial<UserData>): boolean {
const user = this.users.get(id);
if (!user) return false;
const updatedUser = { ...user, ...updates };
this.users.set(id, updatedUser);
this.saveUsers();
return true;
}
deleteUser(id: string): boolean {
const deleted = this.users.delete(id);
if (deleted) {
this.saveUsers();
}
return deleted;
}
}
// π Global declarations using referenced types
declare global {
namespace NodeJS {
interface Global {
userManager: UserManager;
}
}
}
// Initialize global user manager
if (typeof global !== 'undefined') {
global.userManager = new UserManager();
}
export { UserManager, UserData, UserPreferences, NotificationSettings };
ποΈ Advanced Triple-Slash Directive Patterns
// π§ Complex reference management for large projects
/// <reference path="./types/global.d.ts" />
/// <reference path="./types/api.d.ts" />
/// <reference path="./types/database.d.ts" />
/// <reference types="node" />
/// <reference types="express" />
/// <reference lib="es2020" />
/// <reference lib="dom" />
// File: server/application.ts
import express from 'express';
import { DatabaseConnection } from './database/connection';
import { ApiRouter } from './api/router';
import { MiddlewareManager } from './middleware/manager';
import { ConfigurationManager } from './config/manager';
interface ApplicationConfig {
port: number;
host: string;
environment: 'development' | 'staging' | 'production';
database: DatabaseConfig;
api: ApiConfig;
middleware: MiddlewareConfig;
logging: LoggingConfig;
}
interface DatabaseConfig {
type: 'postgres' | 'mysql' | 'mongodb';
host: string;
port: number;
database: string;
username: string;
password: string;
pool: {
min: number;
max: number;
idle: number;
};
}
interface ApiConfig {
version: string;
prefix: string;
rateLimit: {
windowMs: number;
max: number;
};
cors: {
origin: string | string[];
credentials: boolean;
};
}
interface MiddlewareConfig {
compression: boolean;
helmet: boolean;
morgan: boolean;
bodyParser: {
json: { limit: string };
urlencoded: { limit: string; extended: boolean };
};
}
interface LoggingConfig {
level: 'debug' | 'info' | 'warn' | 'error';
format: 'json' | 'combined' | 'common';
file: {
enabled: boolean;
path: string;
maxSize: string;
maxFiles: number;
};
}
class Application {
private app: express.Application;
private server?: import('http').Server;
private database: DatabaseConnection;
private config: ApplicationConfig;
private middlewareManager: MiddlewareManager;
private apiRouter: ApiRouter;
constructor(config: ApplicationConfig) {
this.config = config;
this.app = express();
this.database = new DatabaseConnection(config.database);
this.middlewareManager = new MiddlewareManager(config.middleware);
this.apiRouter = new ApiRouter(config.api);
this.initialize();
}
// π§ Initialize application components
private async initialize(): Promise<void> {
console.log('π Initializing application...');
// Setup middleware
await this.middlewareManager.setup(this.app);
// Setup database connection
await this.database.connect();
// Setup API routes
this.apiRouter.setup(this.app);
// Setup error handling
this.setupErrorHandling();
console.log('β
Application initialized successfully');
}
// π Start the server
async start(): Promise<void> {
try {
await this.initialize();
this.server = this.app.listen(this.config.port, this.config.host, () => {
console.log(`π Server running on ${this.config.host}:${this.config.port}`);
console.log(`π Environment: ${this.config.environment}`);
console.log(`π API: http://${this.config.host}:${this.config.port}${this.config.api.prefix}`);
});
// Setup graceful shutdown
this.setupGracefulShutdown();
} catch (error) {
console.error('β Failed to start server:', error);
process.exit(1);
}
}
// π Stop the server
async stop(): Promise<void> {
console.log('π Stopping application...');
if (this.server) {
await new Promise<void>((resolve) => {
this.server!.close(() => {
console.log('β
HTTP server closed');
resolve();
});
});
}
await this.database.disconnect();
console.log('β
Application stopped gracefully');
}
// π¨ Setup error handling
private setupErrorHandling(): void {
// Handle 404 errors
this.app.use('*', (req, res) => {
res.status(404).json({
error: 'Not Found',
message: `Route ${req.originalUrl} not found`,
statusCode: 404
});
});
// Global error handler
this.app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error('π¨ Unhandled error:', err);
const isDevelopment = this.config.environment === 'development';
res.status(500).json({
error: 'Internal Server Error',
message: isDevelopment ? err.message : 'Something went wrong',
statusCode: 500,
...(isDevelopment && { stack: err.stack })
});
});
}
// π Setup graceful shutdown
private setupGracefulShutdown(): void {
const shutdown = async (signal: string) => {
console.log(`\nπ‘ Received ${signal}, shutting down gracefully...`);
await this.stop();
process.exit(0);
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('π₯ Uncaught Exception:', error);
shutdown('uncaughtException');
});
// Handle unhandled rejections
process.on('unhandledRejection', (reason) => {
console.error('π₯ Unhandled Rejection:', reason);
shutdown('unhandledRejection');
});
}
// π Get application health status
getHealthStatus(): HealthStatus {
return {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
version: process.env.npm_package_version || '1.0.0',
environment: this.config.environment,
database: this.database.isConnected() ? 'connected' : 'disconnected',
memory: process.memoryUsage(),
cpu: process.cpuUsage()
};
}
// π§ Get Express app instance (for testing)
getApp(): express.Application {
return this.app;
}
}
interface HealthStatus {
status: 'healthy' | 'unhealthy';
timestamp: string;
uptime: number;
version: string;
environment: string;
database: 'connected' | 'disconnected';
memory: NodeJS.MemoryUsage;
cpu: NodeJS.CpuUsage;
}
// π― Library-specific directives for different environments
/// <reference path="./environments/development.d.ts" />
/// <reference path="./environments/production.d.ts" />
// Conditional compilation based on environment
declare const __DEVELOPMENT__: boolean;
declare const __PRODUCTION__: boolean;
// Development-only features
if (__DEVELOPMENT__) {
// Additional logging and debugging tools
interface DevelopmentTools {
debugger: DebuggerInterface;
profiler: ProfilerInterface;
hotReload: HotReloadInterface;
}
interface DebuggerInterface {
breakpoint(label?: string): void;
trace(message: string, data?: any): void;
inspect(object: any): void;
}
interface ProfilerInterface {
start(label: string): void;
end(label: string): number;
memory(): NodeJS.MemoryUsage;
}
interface HotReloadInterface {
watch(files: string[]): void;
reload(): void;
isEnabled(): boolean;
}
}
// Production-only optimizations
if (__PRODUCTION__) {
interface ProductionOptimizations {
monitoring: MonitoringInterface;
analytics: AnalyticsInterface;
security: SecurityInterface;
}
interface MonitoringInterface {
reportError(error: Error, context?: any): void;
reportMetric(name: string, value: number): void;
reportEvent(event: string, data?: any): void;
}
interface AnalyticsInterface {
track(event: string, properties?: Record<string, any>): void;
identify(userId: string, traits?: Record<string, any>): void;
page(name: string, properties?: Record<string, any>): void;
}
interface SecurityInterface {
validateRequest(req: express.Request): boolean;
sanitizeInput(input: any): any;
checkRateLimit(ip: string): boolean;
}
}
export { Application, ApplicationConfig, HealthStatus };
// π Example of complex dependency chain
/// <reference path="./base/foundation.ts" />
/// <reference path="./base/utilities.ts" />
/// <reference path="./core/interfaces.ts" />
/// <reference path="./core/abstractions.ts" />
/// <reference path="./services/implementations.ts" />
// File demonstrating layered architecture through references
namespace Architecture {
// ποΈ Foundation layer (referenced first)
export namespace Foundation {
export interface Logger {
debug(message: string): void;
info(message: string): void;
warn(message: string): void;
error(message: string): void;
}
export interface EventEmitter {
on(event: string, listener: Function): void;
emit(event: string, ...args: any[]): void;
off(event: string, listener: Function): void;
}
export interface Disposable {
dispose(): void | Promise<void>;
}
}
// π§ Core layer (depends on foundation)
export namespace Core {
export interface Service extends Foundation.Disposable {
name: string;
version: string;
start(): Promise<void>;
stop(): Promise<void>;
isRunning(): boolean;
}
export interface Repository<T, K = string> {
find(id: K): Promise<T | null>;
findAll(): Promise<T[]>;
save(entity: T): Promise<T>;
delete(id: K): Promise<boolean>;
}
export interface UseCase<TRequest, TResponse> {
execute(request: TRequest): Promise<TResponse>;
}
}
// π― Application layer (depends on core)
export namespace Application {
export interface Controller {
handle(request: any, response: any): Promise<void>;
}
export interface Middleware {
process(context: any, next: Function): Promise<void>;
}
export interface Router {
route(path: string, controller: Controller): void;
use(middleware: Middleware): void;
}
}
// π Infrastructure layer (implements interfaces)
export namespace Infrastructure {
export class DatabaseRepository<T, K = string> implements Core.Repository<T, K> {
constructor(private connection: any) {}
async find(id: K): Promise<T | null> {
// Database implementation
return null;
}
async findAll(): Promise<T[]> {
// Database implementation
return [];
}
async save(entity: T): Promise<T> {
// Database implementation
return entity;
}
async delete(id: K): Promise<boolean> {
// Database implementation
return true;
}
}
export class HttpController implements Application.Controller {
async handle(request: any, response: any): Promise<void> {
// HTTP handling implementation
}
}
export class ExpressRouter implements Application.Router {
private app: express.Application;
constructor(app: express.Application) {
this.app = app;
}
route(path: string, controller: Application.Controller): void {
this.app.all(path, (req, res) => controller.handle(req, res));
}
use(middleware: Application.Middleware): void {
this.app.use((req, res, next) => middleware.process({ req, res }, next));
}
}
}
}
π οΈ Building a Triple-Slash Directive Management System
Letβs create a comprehensive system for managing triple-slash directives in large projects:
// ποΈ Triple-Slash Directive Management and Analysis System
/// <reference types="node" />
/// <reference lib="es2020" />
namespace DirectiveManagement {
// π Core interfaces for directive management
export interface DirectiveRegistry {
files: Map<string, FileDirectives>;
dependencies: Map<string, DependencyInfo>;
libraries: Map<string, LibraryInfo>;
buildOrder: string[];
conflicts: DirectiveConflict[];
metadata: RegistryMetadata;
}
export interface FileDirectives {
filePath: string;
directives: ParsedDirective[];
dependencies: string[];
dependents: string[];
lastModified: Date;
compilationOrder: number;
errors: DirectiveError[];
warnings: DirectiveWarning[];
}
export interface ParsedDirective {
type: DirectiveType;
value: string;
line: number;
column: number;
raw: string;
parsed: DirectiveContent;
}
export type DirectiveType = 'path' | 'types' | 'lib' | 'amd-module' | 'amd-dependency' | 'no-default-lib';
export interface DirectiveContent {
path?: string;
types?: string;
lib?: string;
name?: string;
dependency?: string;
noDefaultLib?: boolean;
}
export interface DependencyInfo {
source: string;
target: string;
type: DirectiveType;
resolved: boolean;
circular: boolean;
depth: number;
}
export interface LibraryInfo {
name: string;
version?: string;
path: string;
files: string[];
dependencies: string[];
usage: LibraryUsage[];
}
export interface LibraryUsage {
file: string;
directive: ParsedDirective;
required: boolean;
}
export interface DirectiveConflict {
type: ConflictType;
files: string[];
directives: ParsedDirective[];
severity: 'error' | 'warning' | 'info';
description: string;
resolution?: ConflictResolution;
}
export type ConflictType =
| 'circular_dependency'
| 'missing_reference'
| 'duplicate_library'
| 'version_mismatch'
| 'invalid_path';
export interface ConflictResolution {
strategy: 'remove' | 'update' | 'reorder' | 'ignore';
details: string;
automatic: boolean;
}
export interface DirectiveError {
type: 'syntax' | 'missing_file' | 'circular_reference' | 'invalid_library';
message: string;
directive: ParsedDirective;
severity: 'error' | 'warning';
}
export interface DirectiveWarning {
type: 'unused_reference' | 'deprecated_library' | 'performance' | 'best_practice';
message: string;
directive?: ParsedDirective;
suggestion?: string;
}
export interface RegistryMetadata {
totalFiles: number;
totalDirectives: number;
lastScanned: Date;
version: string;
projectRoot: string;
}
// π§ Directive Parser and Analyzer
export class DirectiveAnalyzer {
private registry: DirectiveRegistry;
private fileSystem: FileSystemInterface;
private validator: DirectiveValidator;
private resolver: DependencyResolver;
constructor(projectRoot: string) {
this.registry = this.initializeRegistry(projectRoot);
this.fileSystem = new FileSystemAdapter();
this.validator = new DirectiveValidator();
this.resolver = new DependencyResolver();
}
// π Scan project for directives
async scanProject(rootPath: string = this.registry.metadata.projectRoot): Promise<ScanResult> {
console.log(`π Scanning project for triple-slash directives: ${rootPath}`);
try {
const typeScriptFiles = await this.findTypeScriptFiles(rootPath);
const results: FileAnalysisResult[] = [];
for (const filePath of typeScriptFiles) {
const result = await this.analyzeFile(filePath);
results.push(result);
if (result.success) {
this.registry.files.set(filePath, result.directives!);
}
}
// Build dependency graph
this.buildDependencyGraph();
// Detect conflicts
const conflicts = this.detectConflicts();
this.registry.conflicts = conflicts;
// Calculate compilation order
const buildOrder = this.calculateBuildOrder();
this.registry.buildOrder = buildOrder;
// Update metadata
this.updateMetadata();
return {
success: true,
filesScanned: typeScriptFiles.length,
directivesFound: this.getTotalDirectives(),
conflicts: conflicts.length,
buildOrder,
results
};
} catch (error) {
return {
success: false,
error: `Scan failed: ${error.message}`,
filesScanned: 0,
directivesFound: 0,
conflicts: 0,
buildOrder: [],
results: []
};
}
}
// π Analyze single file for directives
async analyzeFile(filePath: string): Promise<FileAnalysisResult> {
console.log(`π Analyzing file: ${filePath}`);
try {
const content = await this.fileSystem.readFile(filePath);
const lines = content.split('\n');
const directives: ParsedDirective[] = [];
const errors: DirectiveError[] = [];
const warnings: DirectiveWarning[] = [];
// Parse directives (only from the top of the file)
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
// Stop parsing if we hit non-directive content
if (line && !line.startsWith('///') && !line.startsWith('//') && !line.startsWith('/*')) {
break;
}
if (line.startsWith('///')) {
const parseResult = this.parseDirective(line, i + 1, 0);
if (parseResult.success) {
directives.push(parseResult.directive!);
} else {
errors.push({
type: 'syntax',
message: parseResult.error!,
directive: parseResult.directive!,
severity: 'error'
});
}
}
}
// Validate directives
const validationResult = this.validator.validate(directives, filePath);
errors.push(...validationResult.errors);
warnings.push(...validationResult.warnings);
// Extract dependencies
const dependencies = this.extractDependencies(directives);
const fileDirectives: FileDirectives = {
filePath,
directives,
dependencies,
dependents: [], // Will be populated during dependency graph building
lastModified: await this.fileSystem.getLastModified(filePath),
compilationOrder: -1, // Will be set during build order calculation
errors,
warnings
};
return {
success: true,
filePath,
directives: fileDirectives
};
} catch (error) {
return {
success: false,
filePath,
error: `Analysis failed: ${error.message}`
};
}
}
// π§© Parse individual directive
private parseDirective(line: string, lineNumber: number, column: number): DirectiveParseResult {
const trimmed = line.substring(3).trim(); // Remove '///'
// Match different directive patterns
const patterns = {
path: /^<reference\s+path\s*=\s*["']([^"']+)["']\s*\/?>$/,
types: /^<reference\s+types\s*=\s*["']([^"']+)["']\s*\/?>$/,
lib: /^<reference\s+lib\s*=\s*["']([^"']+)["']\s*\/?>$/,
amdModule: /^<amd-module\s+name\s*=\s*["']([^"']+)["']\s*\/?>$/,
amdDependency: /^<amd-dependency\s+path\s*=\s*["']([^"']+)["']\s*(?:name\s*=\s*["']([^"']+)["'])?\s*\/?>$/,
noDefaultLib: /^<reference\s+no-default-lib\s*=\s*["']true["']\s*\/?>$/
};
for (const [type, pattern] of Object.entries(patterns)) {
const match = trimmed.match(pattern);
if (match) {
const directive: ParsedDirective = {
type: type as DirectiveType,
value: match[1] || 'true',
line: lineNumber,
column,
raw: line,
parsed: this.createDirectiveContent(type as DirectiveType, match)
};
return { success: true, directive };
}
}
// If no pattern matches, it's an invalid directive
const invalidDirective: ParsedDirective = {
type: 'path', // Default type for error reporting
value: trimmed,
line: lineNumber,
column,
raw: line,
parsed: {}
};
return {
success: false,
directive: invalidDirective,
error: `Invalid triple-slash directive syntax: ${trimmed}`
};
}
// π§ Create directive content object
private createDirectiveContent(type: DirectiveType, match: RegExpMatchArray): DirectiveContent {
switch (type) {
case 'path':
return { path: match[1] };
case 'types':
return { types: match[1] };
case 'lib':
return { lib: match[1] };
case 'amd-module':
return { name: match[1] };
case 'amd-dependency':
return { dependency: match[1], name: match[2] };
case 'no-default-lib':
return { noDefaultLib: true };
default:
return {};
}
}
// π Extract file dependencies from directives
private extractDependencies(directives: ParsedDirective[]): string[] {
const dependencies: string[] = [];
for (const directive of directives) {
if (directive.type === 'path' && directive.parsed.path) {
dependencies.push(directive.parsed.path);
}
}
return dependencies;
}
// π Build dependency graph
private buildDependencyGraph(): void {
console.log('π Building dependency graph');
this.registry.dependencies.clear();
for (const [filePath, fileDirectives] of this.registry.files) {
for (const dependency of fileDirectives.dependencies) {
const resolvedPath = this.resolver.resolvePath(dependency, filePath);
const dependencyInfo: DependencyInfo = {
source: filePath,
target: resolvedPath,
type: 'path',
resolved: this.registry.files.has(resolvedPath),
circular: false, // Will be calculated later
depth: 0 // Will be calculated later
};
const key = `${filePath}->${resolvedPath}`;
this.registry.dependencies.set(key, dependencyInfo);
// Update dependents
const targetFile = this.registry.files.get(resolvedPath);
if (targetFile) {
targetFile.dependents.push(filePath);
}
}
}
// Detect circular dependencies
this.detectCircularDependencies();
}
// π Detect circular dependencies
private detectCircularDependencies(): void {
console.log('π Detecting circular dependencies');
const visited = new Set<string>();
const recursionStack = new Set<string>();
const visit = (filePath: string, path: string[]): boolean => {
if (recursionStack.has(filePath)) {
// Found circular dependency
const cycleStart = path.indexOf(filePath);
const cycle = path.slice(cycleStart).concat(filePath);
this.markCircularDependency(cycle);
return true;
}
if (visited.has(filePath)) {
return false;
}
visited.add(filePath);
recursionStack.add(filePath);
const fileDirectives = this.registry.files.get(filePath);
if (fileDirectives) {
for (const dependency of fileDirectives.dependencies) {
const resolvedPath = this.resolver.resolvePath(dependency, filePath);
if (visit(resolvedPath, [...path, filePath])) {
return true;
}
}
}
recursionStack.delete(filePath);
return false;
};
for (const filePath of this.registry.files.keys()) {
if (!visited.has(filePath)) {
visit(filePath, []);
}
}
}
// π΄ Mark circular dependency
private markCircularDependency(cycle: string[]): void {
for (let i = 0; i < cycle.length - 1; i++) {
const source = cycle[i];
const target = cycle[i + 1];
const key = `${source}->${target}`;
const dependency = this.registry.dependencies.get(key);
if (dependency) {
dependency.circular = true;
}
}
}
// β οΈ Detect conflicts
private detectConflicts(): DirectiveConflict[] {
console.log('β οΈ Detecting directive conflicts');
const conflicts: DirectiveConflict[] = [];
// Check for circular dependencies
for (const [, dependency] of this.registry.dependencies) {
if (dependency.circular) {
conflicts.push({
type: 'circular_dependency',
files: [dependency.source, dependency.target],
directives: [],
severity: 'error',
description: `Circular dependency detected between ${dependency.source} and ${dependency.target}`,
resolution: {
strategy: 'reorder',
details: 'Reorganize file structure to eliminate circular dependencies',
automatic: false
}
});
}
}
// Check for missing references
for (const [, dependency] of this.registry.dependencies) {
if (!dependency.resolved) {
conflicts.push({
type: 'missing_reference',
files: [dependency.source],
directives: [],
severity: 'error',
description: `Missing reference file: ${dependency.target}`,
resolution: {
strategy: 'remove',
details: 'Remove invalid reference or create missing file',
automatic: false
}
});
}
}
// Check for duplicate library references
const libraryRefs = new Map<string, string[]>();
for (const [filePath, fileDirectives] of this.registry.files) {
for (const directive of fileDirectives.directives) {
if (directive.type === 'types' && directive.parsed.types) {
const lib = directive.parsed.types;
if (!libraryRefs.has(lib)) {
libraryRefs.set(lib, []);
}
libraryRefs.get(lib)!.push(filePath);
}
}
}
for (const [lib, files] of libraryRefs) {
if (files.length > 3) { // Threshold for "too many references"
conflicts.push({
type: 'duplicate_library',
files,
directives: [],
severity: 'warning',
description: `Library ${lib} referenced in ${files.length} files - consider centralizing`,
resolution: {
strategy: 'reorder',
details: 'Create a central types file for library references',
automatic: true
}
});
}
}
return conflicts;
}
// π Calculate compilation build order
private calculateBuildOrder(): string[] {
console.log('π Calculating compilation build order');
const visited = new Set<string>();
const buildOrder: string[] = [];
const visit = (filePath: string): void => {
if (visited.has(filePath)) {
return;
}
visited.add(filePath);
const fileDirectives = this.registry.files.get(filePath);
if (fileDirectives) {
// Visit dependencies first
for (const dependency of fileDirectives.dependencies) {
const resolvedPath = this.resolver.resolvePath(dependency, filePath);
if (this.registry.files.has(resolvedPath)) {
visit(resolvedPath);
}
}
// Add this file to build order
buildOrder.push(filePath);
fileDirectives.compilationOrder = buildOrder.length;
}
};
// Visit all files
for (const filePath of this.registry.files.keys()) {
visit(filePath);
}
return buildOrder;
}
// π§ Generate optimized directives
generateOptimizedDirectives(filePath: string): OptimizationResult {
console.log(`π§ Generating optimized directives for: ${filePath}`);
const fileDirectives = this.registry.files.get(filePath);
if (!fileDirectives) {
return {
success: false,
error: `File ${filePath} not found in registry`
};
}
const optimizations: Optimization[] = [];
const optimizedDirectives: ParsedDirective[] = [...fileDirectives.directives];
// Remove unused references
const usedReferences = this.findUsedReferences(filePath);
const unusedDirectives = optimizedDirectives.filter(d =>
d.type === 'path' && !usedReferences.includes(d.parsed.path!)
);
if (unusedDirectives.length > 0) {
optimizations.push({
type: 'remove_unused',
description: `Remove ${unusedDirectives.length} unused references`,
impact: 'positive',
savings: { compilationTime: unusedDirectives.length * 0.1 }
});
// Remove unused directives
unusedDirectives.forEach(directive => {
const index = optimizedDirectives.indexOf(directive);
if (index !== -1) {
optimizedDirectives.splice(index, 1);
}
});
}
// Consolidate library references
const libraryConsolidation = this.consolidateLibraryReferences(optimizedDirectives);
if (libraryConsolidation.changes > 0) {
optimizations.push({
type: 'consolidate_libraries',
description: `Consolidated ${libraryConsolidation.changes} library references`,
impact: 'positive',
savings: { fileSize: libraryConsolidation.changes * 50 }
});
}
// Reorder for optimal compilation
const reorderedDirectives = this.reorderDirectives(optimizedDirectives);
if (this.directivesChanged(optimizedDirectives, reorderedDirectives)) {
optimizations.push({
type: 'reorder',
description: 'Reordered directives for optimal compilation',
impact: 'positive',
savings: { compilationTime: 0.2 }
});
}
const newContent = this.generateDirectiveContent(reorderedDirectives);
return {
success: true,
originalDirectives: fileDirectives.directives,
optimizedDirectives: reorderedDirectives,
optimizations,
newContent,
savings: this.calculateTotalSavings(optimizations)
};
}
// π Generate comprehensive report
generateReport(): DirectiveReport {
console.log('π Generating comprehensive directive report');
const files = Array.from(this.registry.files.values());
const totalDirectives = this.getTotalDirectives();
return {
summary: {
totalFiles: files.length,
totalDirectives,
totalConflicts: this.registry.conflicts.length,
compilationOrder: this.registry.buildOrder.length,
lastScanned: this.registry.metadata.lastScanned
},
fileBreakdown: files.map(f => ({
path: f.filePath,
directiveCount: f.directives.length,
dependencyCount: f.dependencies.length,
dependentCount: f.dependents.length,
hasErrors: f.errors.length > 0,
hasWarnings: f.warnings.length > 0,
compilationOrder: f.compilationOrder
})),
conflicts: this.registry.conflicts,
libraries: this.analyzeLibraryUsage(),
dependencyGraph: this.buildDependencyGraphReport(),
optimizationOpportunities: this.findOptimizationOpportunities(),
recommendations: this.generateRecommendations(),
generatedAt: new Date()
};
}
// π§ Helper methods
private initializeRegistry(projectRoot: string): DirectiveRegistry {
return {
files: new Map(),
dependencies: new Map(),
libraries: new Map(),
buildOrder: [],
conflicts: [],
metadata: {
totalFiles: 0,
totalDirectives: 0,
lastScanned: new Date(),
version: '1.0.0',
projectRoot
}
};
}
private async findTypeScriptFiles(rootPath: string): Promise<string[]> {
return this.fileSystem.findFiles(rootPath, /\.tsx?$/);
}
private getTotalDirectives(): number {
return Array.from(this.registry.files.values())
.reduce((sum, file) => sum + file.directives.length, 0);
}
private updateMetadata(): void {
this.registry.metadata.totalFiles = this.registry.files.size;
this.registry.metadata.totalDirectives = this.getTotalDirectives();
this.registry.metadata.lastScanned = new Date();
}
private findUsedReferences(filePath: string): string[] {
// Implementation would analyze actual usage in the file
return [];
}
private consolidateLibraryReferences(directives: ParsedDirective[]): { changes: number } {
// Implementation would consolidate duplicate library references
return { changes: 0 };
}
private reorderDirectives(directives: ParsedDirective[]): ParsedDirective[] {
// Implementation would reorder directives optimally
return [...directives];
}
private directivesChanged(original: ParsedDirective[], optimized: ParsedDirective[]): boolean {
return original.length !== optimized.length ||
original.some((d, i) => d.raw !== optimized[i]?.raw);
}
private generateDirectiveContent(directives: ParsedDirective[]): string {
return directives.map(d => d.raw).join('\n');
}
private calculateTotalSavings(optimizations: Optimization[]): OptimizationSavings {
return optimizations.reduce((total, opt) => ({
compilationTime: (total.compilationTime || 0) + (opt.savings?.compilationTime || 0),
fileSize: (total.fileSize || 0) + (opt.savings?.fileSize || 0)
}), {});
}
private analyzeLibraryUsage(): LibraryUsageAnalysis[] {
const usage: LibraryUsageAnalysis[] = [];
const libraryMap = new Map<string, string[]>();
for (const [filePath, fileDirectives] of this.registry.files) {
for (const directive of fileDirectives.directives) {
if (directive.type === 'types' && directive.parsed.types) {
const lib = directive.parsed.types;
if (!libraryMap.has(lib)) {
libraryMap.set(lib, []);
}
libraryMap.get(lib)!.push(filePath);
}
}
}
for (const [library, files] of libraryMap) {
usage.push({
library,
usageCount: files.length,
files,
recommended: files.length > 1
});
}
return usage;
}
private buildDependencyGraphReport(): DependencyGraphReport {
return {
totalDependencies: this.registry.dependencies.size,
circularDependencies: Array.from(this.registry.dependencies.values())
.filter(d => d.circular).length,
maxDepth: Math.max(...Array.from(this.registry.dependencies.values())
.map(d => d.depth), 0),
mostConnectedFiles: this.findMostConnectedFiles()
};
}
private findMostConnectedFiles(): Array<{ file: string; connections: number }> {
const connectionCounts = new Map<string, number>();
for (const [filePath, fileDirectives] of this.registry.files) {
const connections = fileDirectives.dependencies.length + fileDirectives.dependents.length;
connectionCounts.set(filePath, connections);
}
return Array.from(connectionCounts.entries())
.map(([file, connections]) => ({ file, connections }))
.sort((a, b) => b.connections - a.connections)
.slice(0, 10);
}
private findOptimizationOpportunities(): OptimizationOpportunity[] {
const opportunities: OptimizationOpportunity[] = [];
// Look for files with many dependencies
for (const [filePath, fileDirectives] of this.registry.files) {
if (fileDirectives.dependencies.length > 5) {
opportunities.push({
type: 'reduce_dependencies',
file: filePath,
description: `File has ${fileDirectives.dependencies.length} dependencies - consider refactoring`,
priority: 'medium',
estimatedImpact: 'moderate'
});
}
}
// Look for circular dependencies
for (const conflict of this.registry.conflicts) {
if (conflict.type === 'circular_dependency') {
opportunities.push({
type: 'eliminate_circular',
file: conflict.files[0],
description: conflict.description,
priority: 'high',
estimatedImpact: 'high'
});
}
}
return opportunities;
}
private generateRecommendations(): string[] {
const recommendations: string[] = [];
if (this.registry.conflicts.length > 0) {
recommendations.push('Resolve directive conflicts to improve compilation reliability');
}
if (this.getTotalDirectives() > 100) {
recommendations.push('Consider using a module bundler to reduce directive complexity');
}
const avgDirectivesPerFile = this.getTotalDirectives() / this.registry.files.size;
if (avgDirectivesPerFile > 3) {
recommendations.push('Review file structure - high average directives per file');
}
return recommendations;
}
}
// π§ Supporting classes and interfaces
class DirectiveValidator {
validate(directives: ParsedDirective[], filePath: string): ValidationResult {
const errors: DirectiveError[] = [];
const warnings: DirectiveWarning[] = [];
// Check for duplicate references
const seen = new Set<string>();
for (const directive of directives) {
const key = `${directive.type}:${directive.value}`;
if (seen.has(key)) {
warnings.push({
type: 'best_practice',
message: `Duplicate ${directive.type} reference: ${directive.value}`,
directive,
suggestion: 'Remove duplicate reference'
});
}
seen.add(key);
}
return { errors, warnings };
}
}
class DependencyResolver {
resolvePath(relativePath: string, fromFile: string): string {
// Implementation would resolve relative paths to absolute paths
// This is a simplified version
if (relativePath.startsWith('./') || relativePath.startsWith('../')) {
// Resolve relative to the directory containing fromFile
const path = require('path');
const dir = path.dirname(fromFile);
return path.resolve(dir, relativePath);
}
return relativePath;
}
}
class FileSystemAdapter implements FileSystemInterface {
async readFile(filePath: string): Promise<string> {
const fs = require('fs').promises;
return fs.readFile(filePath, 'utf8');
}
async getLastModified(filePath: string): Promise<Date> {
const fs = require('fs').promises;
const stats = await fs.stat(filePath);
return stats.mtime;
}
async findFiles(rootPath: string, pattern: RegExp): Promise<string[]> {
const fs = require('fs').promises;
const path = require('path');
const files: string[] = [];
const scan = async (dir: string): Promise<void> => {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
await scan(fullPath);
} else if (entry.isFile() && pattern.test(entry.name)) {
files.push(fullPath);
}
}
};
await scan(rootPath);
return files;
}
}
// π Supporting interfaces
interface FileSystemInterface {
readFile(filePath: string): Promise<string>;
getLastModified(filePath: string): Promise<Date>;
findFiles(rootPath: string, pattern: RegExp): Promise<string[]>;
}
interface ScanResult {
success: boolean;
filesScanned: number;
directivesFound: number;
conflicts: number;
buildOrder: string[];
results: FileAnalysisResult[];
error?: string;
}
interface FileAnalysisResult {
success: boolean;
filePath: string;
directives?: FileDirectives;
error?: string;
}
interface DirectiveParseResult {
success: boolean;
directive: ParsedDirective;
error?: string;
}
interface ValidationResult {
errors: DirectiveError[];
warnings: DirectiveWarning[];
}
interface OptimizationResult {
success: boolean;
originalDirectives?: ParsedDirective[];
optimizedDirectives?: ParsedDirective[];
optimizations?: Optimization[];
newContent?: string;
savings?: OptimizationSavings;
error?: string;
}
interface Optimization {
type: 'remove_unused' | 'consolidate_libraries' | 'reorder' | 'merge_files';
description: string;
impact: 'positive' | 'neutral' | 'negative';
savings?: OptimizationSavings;
}
interface OptimizationSavings {
compilationTime?: number;
fileSize?: number;
dependencies?: number;
}
interface DirectiveReport {
summary: ReportSummary;
fileBreakdown: FileBreakdown[];
conflicts: DirectiveConflict[];
libraries: LibraryUsageAnalysis[];
dependencyGraph: DependencyGraphReport;
optimizationOpportunities: OptimizationOpportunity[];
recommendations: string[];
generatedAt: Date;
}
interface ReportSummary {
totalFiles: number;
totalDirectives: number;
totalConflicts: number;
compilationOrder: number;
lastScanned: Date;
}
interface FileBreakdown {
path: string;
directiveCount: number;
dependencyCount: number;
dependentCount: number;
hasErrors: boolean;
hasWarnings: boolean;
compilationOrder: number;
}
interface LibraryUsageAnalysis {
library: string;
usageCount: number;
files: string[];
recommended: boolean;
}
interface DependencyGraphReport {
totalDependencies: number;
circularDependencies: number;
maxDepth: number;
mostConnectedFiles: Array<{ file: string; connections: number }>;
}
interface OptimizationOpportunity {
type: 'reduce_dependencies' | 'eliminate_circular' | 'consolidate_references' | 'remove_unused';
file: string;
description: string;
priority: 'low' | 'medium' | 'high';
estimatedImpact: 'low' | 'moderate' | 'high';
}
}
// π Usage examples with the management system
const analyzer = new DirectiveManagement.DirectiveAnalyzer('./src');
// Scan entire project
const scanResult = await analyzer.scanProject();
if (scanResult.success) {
console.log(`Scanned ${scanResult.filesScanned} files`);
console.log(`Found ${scanResult.directivesFound} directives`);
console.log(`Detected ${scanResult.conflicts} conflicts`);
console.log('Build order:', scanResult.buildOrder);
} else {
console.error('Scan failed:', scanResult.error);
}
// Analyze specific file
const fileResult = await analyzer.analyzeFile('./src/main.ts');
if (fileResult.success) {
console.log('File analysis:', fileResult.directives);
} else {
console.error('File analysis failed:', fileResult.error);
}
// Generate optimizations
const optimization = analyzer.generateOptimizedDirectives('./src/main.ts');
if (optimization.success) {
console.log('Optimizations:', optimization.optimizations);
console.log('Savings:', optimization.savings);
} else {
console.error('Optimization failed:', optimization.error);
}
// Generate comprehensive report
const report = analyzer.generateReport();
console.log('Directive analysis report:', report);
π― Conclusion
Congratulations! Youβve now mastered the sophisticated art of triple-slash directives in TypeScript! π
Throughout this tutorial, youβve learned how to:
- Master all triple-slash directive types including path references, type references, library references, and AMD directives
- Control compilation order and dependencies effectively to ensure proper build sequences
- Create sophisticated management systems for tracking and optimizing directives in large projects
- Handle complex scenarios including circular dependencies, library consolidation, and build optimization
- Build maintainable architectures that scale effectively with project growth
Triple-slash directives are the unsung heroes of TypeScript compilation, providing precise control over how your files are processed and how dependencies are resolved. When used correctly, they enable you to create well-organized, efficiently compiled projects that maintain clear dependency relationships and optimal build performance.
Remember: triple-slash directives are about orchestration and control, not replacement of modern module systems. Use them judiciously to complement your module architecture, especially in scenarios involving declaration files, library integration, and complex build requirements. With the management system and optimization strategies youβve learned, you can confidently use triple-slash directives to create maintainable, efficient TypeScript projects.
Keep practicing these patterns, and youβll find that triple-slash directives become a powerful tool for fine-tuning your TypeScript build process and creating sophisticated, well-organized codebases! π