Prerequisites
- TypeScript installed on your system โก
- Basic understanding of TypeScript syntax ๐
- Command line familiarity ๐ป
What you'll learn
- Use the TypeScript compiler effectively ๐ฏ
- Understand all major compiler options ๐๏ธ
- Optimize compilation for your projects ๐
- Debug compilation issues like a pro โจ
๐ฏ Introduction
Welcome to the heart of TypeScript - the compiler! ๐ In this guide, weโll explore the powerful tsc
command that transforms your TypeScript code into JavaScript that runs everywhere.
Youโll discover how to harness the compilerโs full potential, from basic compilation to advanced optimizations. Whether youโre building a small script or a large application, understanding the compiler is key to TypeScript mastery!
By the end of this tutorial, youโll be commanding tsc
like a TypeScript wizard! Letโs compile! ๐งโโ๏ธ
๐ Understanding the TypeScript Compiler
๐ค What is tsc?
The TypeScript Compiler (tsc
) is like a master translator ๐ that speaks both TypeScript and JavaScript fluently. Think of it as a careful editor who not only translates your code but also checks for errors, suggests improvements, and ensures everything makes sense!
The compiler:
- โจ Transforms TypeScript to JavaScript
- ๐ Performs type checking
- ๐ก๏ธ Catches errors before runtime
- ๐ Generates declaration files
๐ก Why Master the Compiler?
Hereโs why understanding tsc
is crucial:
- Better Builds ๐๏ธ: Optimize for development or production
- Faster Feedback โก: Catch errors immediately
- Team Consistency ๐ฅ: Share compiler settings
- Debugging Power ๐: Understand whatโs happening
- Performance Gains ๐: Compile only whatโs needed
Real-world example: Imagine shipping code with a type error ๐. The compiler catches it during build, saving you from a production bug and a midnight fix! ๐
๐ง Basic Compiler Usage
๐ Simple Compilation
Letโs start with the basics:
# ๐ฏ Compile a single file
tsc hello.ts
# ๐ This creates hello.js
# TypeScript โ JavaScript transformation complete!
Create a sample file to compile:
// ๐ hello.ts
interface Greeting {
message: string;
emoji: string;
timestamp: Date;
}
class Greeter {
private greetings: Greeting[] = [];
// ๐ Add a greeting
greet(message: string, emoji: string = "๐"): void {
const greeting: Greeting = {
message,
emoji,
timestamp: new Date()
};
this.greetings.push(greeting);
console.log(`${emoji} ${message}`);
}
// ๐ Show greeting history
showHistory(): void {
console.log("๐ Greeting History:");
this.greetings.forEach((g, i) => {
console.log(` ${i + 1}. ${g.emoji} ${g.message} (${g.timestamp.toLocaleTimeString()})`);
});
}
}
// ๐ฎ Use the greeter
const greeter = new Greeter();
greeter.greet("Hello, TypeScript!", "๐");
greeter.greet("Compiler is awesome!", "๐");
greeter.showHistory();
๐ฏ Compiler Options
Essential compiler flags:
# ๐ Type check without emitting files
tsc --noEmit hello.ts
# ๐ Specify output directory
tsc --outDir dist hello.ts
# ๐ฏ Target specific ECMAScript version
tsc --target ES2020 hello.ts
# ๐ก๏ธ Enable strict type checking
tsc --strict hello.ts
# ๐ Generate source maps for debugging
tsc --sourceMap hello.ts
# ๐ Watch mode - recompile on changes
tsc --watch hello.ts
๐ก Practical Examples
๐ Example 1: Multi-File Project Compilation
Letโs compile a real project structure:
// ๐ src/models/product.ts
export interface Product {
id: string;
name: string;
price: number;
inStock: boolean;
emoji: string;
}
export class ProductValidator {
// โ
Validate product data
static validate(product: any): product is Product {
return (
typeof product.id === "string" &&
typeof product.name === "string" &&
typeof product.price === "number" &&
typeof product.inStock === "boolean" &&
typeof product.emoji === "string"
);
}
// ๐ฐ Validate price
static validatePrice(price: number): boolean {
if (price < 0) {
console.log("โ Price cannot be negative!");
return false;
}
if (price > 999999) {
console.log("โ Price seems unrealistic!");
return false;
}
return true;
}
}
// ๐ src/services/inventory.ts
import { Product, ProductValidator } from '../models/product';
export class InventoryService {
private products: Map<string, Product> = new Map();
// โ Add product to inventory
addProduct(product: Product): boolean {
if (!ProductValidator.validate(product)) {
console.log("โ Invalid product data!");
return false;
}
if (!ProductValidator.validatePrice(product.price)) {
return false;
}
this.products.set(product.id, product);
console.log(`โ
Added ${product.emoji} ${product.name} to inventory!`);
return true;
}
// ๐ Get inventory summary
getSummary(): void {
console.log("\n๐ฆ Inventory Summary:");
console.log(`Total products: ${this.products.size}`);
let totalValue = 0;
let inStockCount = 0;
this.products.forEach(product => {
totalValue += product.price;
if (product.inStock) inStockCount++;
});
console.log(`๐ฐ Total value: $${totalValue.toFixed(2)}`);
console.log(`โ
In stock: ${inStockCount}/${this.products.size}`);
}
}
// ๐ src/index.ts
import { InventoryService } from './services/inventory';
import { Product } from './models/product';
// ๐ฏ Create inventory system
const inventory = new InventoryService();
// ๐๏ธ Add some products
const products: Product[] = [
{ id: "1", name: "TypeScript Book", price: 29.99, inStock: true, emoji: "๐" },
{ id: "2", name: "Mechanical Keyboard", price: 149.99, inStock: true, emoji: "โจ๏ธ" },
{ id: "3", name: "Coffee Mug", price: 12.99, inStock: false, emoji: "โ" },
{ id: "4", name: "Monitor Stand", price: 39.99, inStock: true, emoji: "๐ฅ๏ธ" }
];
// ๐ฆ Process products
console.log("๐ช Setting up inventory...\n");
products.forEach(product => inventory.addProduct(product));
// ๐ Show summary
inventory.getSummary();
Compile the entire project:
# ๐๏ธ Compile all TypeScript files
tsc src/**/*.ts --outDir dist
# ๐ฏ With module resolution
tsc src/index.ts --outDir dist --module commonjs --moduleResolution node
# ๐ฆ With declaration files
tsc src/**/*.ts --outDir dist --declaration
# ๐ Production build
tsc src/**/*.ts --outDir dist --target ES2020 --removeComments --sourceMap false
๐ฎ Example 2: Watch Mode Development
Create a development workflow:
// ๐ dev-server.ts
interface ServerConfig {
port: number;
host: string;
cors: boolean;
emoji: string;
}
interface Route {
path: string;
method: "GET" | "POST" | "PUT" | "DELETE";
handler: () => string;
emoji: string;
}
class DevServer {
private config: ServerConfig;
private routes: Route[] = [];
private requestCount: number = 0;
constructor(config: ServerConfig) {
this.config = config;
console.log(`${config.emoji} Dev server configured on ${config.host}:${config.port}`);
}
// ๐ฃ๏ธ Add route
addRoute(route: Route): void {
this.routes.push(route);
console.log(`โ
Added route: ${route.emoji} ${route.method} ${route.path}`);
}
// ๐ Start server (simulated)
start(): void {
console.log(`\n๐ Server starting...`);
console.log(`๐ก Listening on http://${this.config.host}:${this.config.port}`);
console.log(`๐ CORS: ${this.config.cors ? "Enabled" : "Disabled"}`);
// Simulate requests
this.simulateTraffic();
}
// ๐ฎ Simulate incoming requests
private simulateTraffic(): void {
console.log("\n๐ Simulating traffic:");
// Simulate some requests
const mockRequests = [
{ path: "/api/health", method: "GET" as const },
{ path: "/api/users", method: "GET" as const },
{ path: "/api/products", method: "POST" as const }
];
mockRequests.forEach((req, i) => {
setTimeout(() => {
this.handleRequest(req.path, req.method);
}, (i + 1) * 1000);
});
}
// ๐จ Handle request
private handleRequest(path: string, method: Route["method"]): void {
this.requestCount++;
const route = this.routes.find(r => r.path === path && r.method === method);
if (route) {
const response = route.handler();
console.log(`โ
[${this.requestCount}] ${route.emoji} ${method} ${path} โ ${response}`);
} else {
console.log(`โ [${this.requestCount}] 404 ${method} ${path} - Route not found!`);
}
}
}
// ๐ฏ Create and configure server
const server = new DevServer({
port: 3000,
host: "localhost",
cors: true,
emoji: "๐ฅ๏ธ"
});
// ๐ฃ๏ธ Define routes
server.addRoute({
path: "/api/health",
method: "GET",
handler: () => "200 OK - Healthy! ๐",
emoji: "๐ฅ"
});
server.addRoute({
path: "/api/users",
method: "GET",
handler: () => "200 OK - Users list ๐ฅ",
emoji: "๐ค"
});
server.addRoute({
path: "/api/products",
method: "POST",
handler: () => "201 Created - Product added! ๐ฆ",
emoji: "๐๏ธ"
});
// ๐ Start the server
server.start();
Use watch mode for development:
# ๐ Start watch mode
tsc --watch dev-server.ts
# ๐ฏ Watch with specific options
tsc --watch --preserveWatchOutput --pretty
# ๐ Watch entire project
tsc --watch --project .
# ๐ก Output:
# [11:30:00 AM] Starting compilation in watch mode...
# [11:30:01 AM] Found 0 errors. Watching for file changes.
#
# ๐ Make a change...
# [11:30:15 AM] File change detected. Starting incremental compilation...
# [11:30:15 AM] Found 0 errors. Watching for file changes.
๐ Advanced Compiler Options
๐งโโ๏ธ Optimization Options
# ๐ฏ Remove comments for smaller output
tsc --removeComments
# ๐ฆ Bundle into single file (with module system)
tsc --outFile bundle.js --module amd
# ๐๏ธ Skip lib checking for faster builds
tsc --skipLibCheck
# โก Incremental compilation
tsc --incremental
# ๐จ Pretty error messages
tsc --pretty
# ๐ List all emitted files
tsc --listEmittedFiles
๐๏ธ Project References
// ๐ packages/core/tsconfig.json
{
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "./src"
}
}
// ๐ packages/app/tsconfig.json
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"references": [
{ "path": "../core" }
]
}
Build with references:
# ๐๏ธ Build entire project graph
tsc --build
# ๐งน Clean build outputs
tsc --build --clean
# ๐ Force rebuild
tsc --build --force
# ๐ Verbose output
tsc --build --verbose
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Module Resolution Issues
// โ Cannot find module error
import { utils } from 'utils'; // Where is utils?
// โ
Solution 1: Use relative imports
import { utils } from './utils';
// โ
Solution 2: Configure paths in tsconfig
// tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@utils/*": ["src/utils/*"]
}
}
}
// Now you can:
import { utils } from '@utils/helpers';
๐คฏ Pitfall 2: Slow Compilation
# โ Compiling everything every time
tsc src/**/*.ts
# โ
Solution: Use incremental compilation
tsc --incremental
# โ
Creates tsconfig.tsbuildinfo for faster rebuilds
# Second compilation will be much faster!
# ๐ก Also use:
tsc --skipLibCheck # Skip type checking of .d.ts files
tsc --excludeFiles "**/node_modules/**" # Exclude unnecessary files
๐ต Pitfall 3: Output Structure Problems
# โ Files scattered everywhere
tsc file1.ts file2.ts file3.ts
# Creates file1.js, file2.js, file3.js in same locations
# โ
Solution: Use proper project structure
tsc --project . --outDir dist
# ๐ Results in clean structure:
# dist/
# โโโ models/
# โโโ services/
# โโโ index.js
๐ ๏ธ Best Practices
- ๐ฏ Always use tsconfig.json: Donโt rely on CLI flags alone
- ๐ Enable strict mode: Start strict, relax if needed
- ๐ก๏ธ Use incremental builds: Faster compilation
- ๐จ Organize output: Use outDir for clean structure
- โจ Watch during development: Instant feedback
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Compilation Pipeline
Create a build system that:
๐ Requirements:
- โ Compiles TypeScript with different configs for dev/prod
- ๐ท๏ธ Generates declaration files
- ๐ค Validates code before compilation
- ๐ Reports compilation statistics
- ๐จ Supports watch mode for development
๐ Bonus Points:
- Add custom pre/post compilation scripts
- Implement parallel compilation for multiple packages
- Create a build performance dashboard
๐ก Solution
๐ Click to see solution
// ๐ build-system.ts
import { execSync } from 'child_process';
interface BuildConfig {
mode: "development" | "production";
watch: boolean;
generateDts: boolean;
sourceMap: boolean;
minify: boolean;
}
interface BuildStats {
startTime: Date;
endTime?: Date;
duration?: number;
filesCompiled: number;
errors: number;
warnings: number;
}
class BuildSystem {
private stats: BuildStats = {
startTime: new Date(),
filesCompiled: 0,
errors: 0,
warnings: 0
};
// ๐๏ธ Build project
build(config: BuildConfig): void {
console.log(`๐๏ธ Starting ${config.mode} build...\n`);
// Reset stats
this.stats = {
startTime: new Date(),
filesCompiled: 0,
errors: 0,
warnings: 0
};
try {
// ๐งน Clean previous build
this.clean();
// โ
Run pre-build validation
this.validate();
// ๐ ๏ธ Compile TypeScript
this.compile(config);
// ๐ Show statistics
this.showStats();
} catch (error) {
console.error("โ Build failed!", error);
this.stats.errors++;
}
}
// ๐งน Clean build directory
private clean(): void {
console.log("๐งน Cleaning build directory...");
try {
execSync('rm -rf dist', { stdio: 'pipe' });
console.log("โ
Clean complete\n");
} catch {
// Directory might not exist
}
}
// โ
Validate code
private validate(): void {
console.log("๐ Validating code...");
try {
// Type check without emitting
execSync('tsc --noEmit', { stdio: 'pipe' });
console.log("โ
Validation passed\n");
} catch (error) {
console.log("โ Validation failed!");
throw error;
}
}
// ๐ ๏ธ Compile TypeScript
private compile(config: BuildConfig): void {
console.log("๐ ๏ธ Compiling TypeScript...");
// Build command based on config
let command = 'tsc';
const flags: string[] = [];
// Output directory
flags.push('--outDir dist');
// Mode-specific options
if (config.mode === 'production') {
flags.push('--target ES2020');
flags.push('--removeComments');
flags.push('--strict');
} else {
flags.push('--target ES2018');
flags.push('--pretty');
}
// Optional features
if (config.generateDts) {
flags.push('--declaration');
flags.push('--declarationDir dist/types');
}
if (config.sourceMap) {
flags.push('--sourceMap');
}
if (config.watch) {
flags.push('--watch');
flags.push('--preserveWatchOutput');
}
// Incremental compilation
flags.push('--incremental');
// Execute compilation
const fullCommand = `${command} ${flags.join(' ')}`;
console.log(`๐ Command: ${fullCommand}\n`);
try {
if (config.watch) {
console.log("๐ Watch mode enabled - Press Ctrl+C to stop\n");
execSync(fullCommand, { stdio: 'inherit' });
} else {
const output = execSync(fullCommand, { encoding: 'utf-8' });
console.log(output);
console.log("โ
Compilation complete!\n");
}
// Count compiled files (simplified)
this.stats.filesCompiled = 10; // In real implementation, parse output
} catch (error) {
this.stats.errors++;
throw error;
}
}
// ๐ Show build statistics
private showStats(): void {
this.stats.endTime = new Date();
this.stats.duration = this.stats.endTime.getTime() - this.stats.startTime.getTime();
console.log("๐ Build Statistics:");
console.log("โโโโโโโโโโโโโโโโโโโ");
console.log(`โฑ๏ธ Duration: ${this.stats.duration}ms`);
console.log(`๐ Files compiled: ${this.stats.filesCompiled}`);
console.log(`โ Errors: ${this.stats.errors}`);
console.log(`โ ๏ธ Warnings: ${this.stats.warnings}`);
console.log(`โ
Status: ${this.stats.errors === 0 ? 'SUCCESS' : 'FAILED'}`);
}
}
// ๐ฎ Demo the build system
const builder = new BuildSystem();
// Development build
console.log("๐ TypeScript Build System Demo\n");
builder.build({
mode: "development",
watch: false,
generateDts: false,
sourceMap: true,
minify: false
});
// Production build config (example)
const productionConfig: BuildConfig = {
mode: "production",
watch: false,
generateDts: true,
sourceMap: false,
minify: true
};
console.log("\n๐ฆ Production config ready:");
console.log(JSON.stringify(productionConfig, null, 2));
๐ Key Takeaways
Youโve mastered the TypeScript compiler! Hereโs what you can now do:
- โ Use tsc with confidence for any project ๐ช
- โ Configure compilation for development and production ๐ก๏ธ
- โ Optimize build times with advanced options ๐ฏ
- โ Debug compilation issues effectively ๐
- โ Create efficient build pipelines! ๐
Remember: The compiler is your friend - configure it well and it will catch bugs before they bite! ๐ก๏ธ
๐ค Next Steps
Congratulations! ๐ Youโre now a TypeScript compiler expert!
Hereโs what to do next:
- ๐ป Experiment with different compiler options
- ๐๏ธ Set up a build pipeline for your project
- ๐ Explore tsconfig.json configuration (next tutorial!)
- ๐ Share your build optimizations!
Remember: A well-configured compiler is the foundation of great TypeScript projects! ๐๏ธ
Happy coding! ๐๐โจ