Welcome to the InGen genetic laboratory! Just as the park's scientists transform ancient DNA sequences into living dinosaurs, the TypeScript compiler transforms TypeScript code into standard JavaScript that can run in a browser or Node.js environment.
Dr. Wu, the park's chief geneticist, often says: "We give life to code from millions of years ago." Similarly, the TypeScript compiler lets us write code in a modern language that is then transformed into a more "primitive" form of JavaScript, understandable by all environments.
The TypeScript compiler (usually called tsc - TypeScript Compiler) is a tool that analyzes your TypeScript code, checks types, and then generates corresponding JavaScript code. It is a bridge between the world of statically typed TypeScript and dynamically typed JavaScript.
In Jurassic Park terminology, we can say that:
Before we start genetic experiments, we need the right equipment:
1# Global installation (gives access to the tsc command system-wide)
2npm install -g typescript
3
4# Local installation (only within the project)
5npm install --save-dev typescriptCompiling a single TypeScript file is like creating a single dinosaur in the laboratory:
1# Syntax: tsc [filename].ts
2tsc security-system.tsAfter running this command, a JavaScript file with the same name will be created:
security-system.js.In a real Jurassic Park we don't create individual dinosaurs - we build an entire ecosystem. Similarly, in larger projects we compile many files at once:
1# Compile the entire project according to tsconfig.json
2tsc
3
4# Compile with watch mode - the compiler will watch for changes and recompile automatically
5tsc --watchThe TypeScript compilation process proceeds in several steps, similar to the dinosaur creation process in the laboratory:
This process can be compared to the stages of creating a dinosaur:
The TypeScript compiler offers many command-line options that allow for precise control of the compilation process:
1# Specifying the output directory
2tsc --outDir ./dist
3
4# Generating type declaration files (.d.ts)
5tsc --declaration
6
7# Generating source maps (.js.map) for easier debugging
8tsc --sourceMap
9
10# Merging multiple output files into one (deprecated for ES modules)
11tsc --outFile ./dist/bundle.js1# Compile without emitting files (type checking only)
2tsc --noEmit
3
4# Compile despite errors
5tsc --noEmitOnError false
6
7# Enable strict mode
8tsc --strict
9
10# Compile in watch mode (automatic recompilation on changes)
11tsc --watch1# Specifying the target JavaScript version (ES5, ES6, etc.)
2tsc --target ES2015
3
4# Specifying the module system (commonjs, amd, system, esm, etc.)
5tsc --module commonjs
6
7# Preserving directory structure
8tsc --rootDir ./src1# Displaying detailed information about the compilation process
2tsc --verbose
3
4# Listing all files that are part of the compilation
5tsc --listFiles
6
7# Displaying the resulting configuration
8tsc --showConfigLet's see how the compiler transforms specific TypeScript constructs to JavaScript:
1// TypeScript code (security-types.ts)
2interface Employee {
3 id: number;
4 name: string;
5 role: string;
6 accessLevel: number;
7}
8
9type AccessLevel = 1 | 2 | 3 | 4 | 5;
10
11function checkAccess(employee: Employee, requiredLevel: AccessLevel): boolean {
12 return employee.accessLevel >= requiredLevel;
13}1// Compiled JavaScript (security-types.js)
2function checkAccess(employee, requiredLevel) {
3 return employee.accessLevel >= requiredLevel;
4}Interfaces and types are used only during type checking and are completely removed from JavaScript code. It's like dinosaur DNA - important during creation, but invisible in the final creature.
1// TypeScript code (employee.ts)
2function createEmployee(name: string, role: string, accessLevel: number): Employee {
3 return {
4 id: generateId(),
5 name,
6 role,
7 accessLevel
8 };
9}
10
11let newEmployee: Employee;1// Compiled JavaScript (employee.js)
2function createEmployee(name, role, accessLevel) {
3 return {
4 id: generateId(),
5 name,
6 role,
7 accessLevel
8 };
9}
10
11let newEmployee;Type annotations disappear, but the code structure remains the same. It's like a genetic map used to create a dinosaur, but not transferred to the final creature.
1// TypeScript code (tracking-system.ts)
2// Using optional chaining and nullish coalescing operator
3function getLocationName(dinosaur?: Dinosaur) {
4 return dinosaur?.currentLocation?.name ?? "Unknown Location";
5}
6
7// Using rest parameters and destructuring
8function analyzeDinosaurs(...dinosaurs: Dinosaur[]) {
9 const [mostDangerous, ...others] = dinosaurs.sort((a, b) => b.dangerLevel - a.dangerLevel);
10 return { mostDangerous, others };
11}Compilation may look different depending on the target ES version. For ES5 it might look like this:
1// Compiled JavaScript (tracking-system.js) for ES5
2function getLocationName(dinosaur) {
3 var _a, _b;
4 return (_b = (_a = dinosaur === null || dinosaur === void 0 ? void 0 : dinosaur.currentLocation) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : "Unknown Location";
5}
6
7function analyzeDinosaurs() {
8 var dinosaurs = [];
9 for (var _i = 0; _i < arguments.length; _i++) {
10 dinosaurs[_i] = arguments[_i];
11 }
12 var sortedDinosaurs = dinosaurs.sort(function (a, b) { return b.dangerLevel - a.dangerLevel; });
13 var mostDangerous = sortedDinosaurs[0], others = sortedDinosaurs.slice(1);
14 return { mostDangerous: mostDangerous, others: others };
15}Here the compiler had to do much more work to transform modern JavaScript features into ES5-compatible code. It's like adapting "primitive" genetic code to work in a modern environment.
1// TypeScript code (dinosaur.ts)
2class Dinosaur {
3 private _healthStatus: number = 100;
4
5 constructor(
6 public readonly species: string,
7 public age: number,
8 private readonly dangerLevel: number
9 ) {}
10
11 get health(): number {
12 return this._healthStatus;
13 }
14
15 feed(amount: number): void {
16 this._healthStatus = Math.min(100, this._healthStatus + amount);
17 console.log(`${this.species} has been fed. New health: ${this._healthStatus}%`);
18 }
19}1// Compiled JavaScript (dinosaur.js) for ES2015
2class Dinosaur {
3 constructor(species, age, dangerLevel) {
4 this.species = species;
5 this.age = age;
6 this.dangerLevel = dangerLevel;
7 this._healthStatus = 100;
8 }
9
10 get health() {
11 return this._healthStatus;
12 }
13
14 feed(amount) {
15 this._healthStatus = Math.min(100, this._healthStatus + amount);
16 console.log(`${this.species} has been fed. New health: ${this._healthStatus}%`);
17 }
18}For ES5, classes will be transformed into constructor functions and prototypes.
In the InGen laboratory, detailed documentation is kept for all created species. In the TypeScript world, the equivalent of such documentation is type declaration files (
.d.ts).These files contain only type information, without implementation. They are used by IDEs and the compiler to provide code hints and type checking.
1// dinosaur.d.ts - type declaration file for the Dinosaur class
2declare class Dinosaur {
3 readonly species: string;
4 age: number;
5 private readonly dangerLevel: number;
6 private _healthStatus: number;
7
8 constructor(species: string, age: number, dangerLevel: number);
9
10 get health(): number;
11 feed(amount: number): void;
12}To generate type declaration files for your project, you can use the
--declaration option:1tsc --declarationDebugging dinosaurs can be complicated - we don't always know which DNA sequence corresponds to a given behavior. Similarly, debugging compiled JavaScript code can be difficult without source maps.
Source maps are
.js.map` files that allow the browser or Node.js environment to map compiled JavaScript code back to the original TypeScript code. This allows you to debug TypeScript code directly, even when JavaScript is running.To generate source maps, use the
--sourceMap option:1tsc --sourceMapIn a large park full of different dinosaur species, we need detailed plans and specifications. Similarly, in larger TypeScript projects we use the
tsconfig.json file to configure the compiler.This file allows you to set all compiler options in one place, which is much more convenient than passing them every time on the command line.
1# Creating a basic tsconfig.json file
2tsc --init
3
4# Compiling the project according to the configuration
5tscDr. Wu sometimes has to ignore certain genetic incompatibilities so that a dinosaur can survive. Similarly, when migrating an existing JavaScript project to TypeScript, we may need to temporarily ignore some type errors.
1// @ts-ignore
2element.style.background = 'red'; // TypeScript would report an error, but we ignore it
3
4// @ts-expect-error
5console.log(dinosaur.location); // We document that we expect an error hereany type1function legacyFunction(data: any) {
2 // We temporarily use 'any', but plan to fix this later
3 return data.process();
4}We can also gradually enable type checking throughout the project, starting with less strict settings:
1// tsconfig.json with less strict settings
2{
3 "compilerOptions": {
4 "allowJs": true, // Allows compiling .js files
5 "checkJs": false, // Doesn't check types in .js files
6 "noImplicitAny": false, // Allows implicit 'any' type
7 "strictNullChecks": false // Doesn't require checking null/undefined
8 }
9}Over time, as the project becomes more "tamed", we can gradually increase the strictness of type checking.
Problem: A global TypeScript installation may conflict with the project's local version.
Solution: Use the locally installed compiler via npx:
1npx tscProblem: The compiler can't find files or compiles the wrong files.
Solution: Check the
include, exclude, rootDir and outDir settings in tsconfig.json:1{
2 "compilerOptions": {
3 "rootDir": "./src",
4 "outDir": "./dist"
5 },
6 "include": ["src/**/*"],
7 "exclude": ["node_modules", "**/*.spec.ts"]
8}Problem: TypeScript reports errors about types in external libraries.
Solution: Install the appropriate type definition files or create your own:
1npm install --save-dev @types/libraryOr create your own definition file:
1// types/missing-library.d.ts
2declare module 'missing-library' {
3 export function someFunction(): void;
4}Problem: TypeScript has problems with inferred types in complex generic structures.
Solution: Provide explicit type annotations at strategic locations:
1// Instead of letting TypeScript infer the type:
2const result = complexFunction(data);
3
4// Provide an explicit type annotation:
5const result: ExpectedReturnType = complexFunction(data);For the most advanced scenarios, TypeScript provides a compilation API that allows deep integration with developer tools. It's like access to advanced laboratory tools for particularly demanding genetic experiments.
1import * as ts from 'typescript';
2
3// We create a compiler host
4const host = ts.createCompilerHost({});
5
6// We create a TypeScript program
7const program = ts.createProgram(['file1.ts', 'file2.ts'], {
8 target: ts.ScriptTarget.ES2015,
9 module: ts.ModuleKind.CommonJS
10}, host);
11
12// We compile the program
13const emitResult = program.emit();
14
15// We get the diagnostics (errors and warnings)
16const diagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);
17
18// We display the diagnostics
19diagnostics.forEach(diagnostic => {
20 if (diagnostic.file) {
21 const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
22 const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '
23');
24 console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
25 } else {
26 console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, '
27'));
28 }
29});This functionality is used by tools such as webpack, Rollup, or ESBuild to integrate TypeScript compilation into their own build processes.
The TypeScript compiler, like the InGen genetic laboratory, transforms advanced, safe code (TypeScript) into a form that can be "brought to life" in a browser or Node.js environment (JavaScript).
Key points:
The TypeScript compiler (
tsc) transforms TypeScript code into JavaScript, removing type annotations and transforming advanced features.The compilation process includes: scanning, parsing, type checking, transformations and emission.
There are many compiler options that can be passed on the command line or configured in
tsconfig.json.Every TypeScript feature has a way of compiling to JavaScript, with the complexity of this compilation depending on the target JavaScript version.
.d.tsfiles and source maps.js.map help with documentation and debugging.When migrating existing projects, TypeScript allows gradual introduction of types.
For advanced scenarios, there is a TypeScript compilation API.
Just as the InGen laboratory needs precise protocols and tools to create dinosaurs, programmers need a well-configured TypeScript compiler to create safe and efficient JavaScript applications.
In the next lesson we'll dive into the basic types available in TypeScript - it will be like studying different dinosaur species in our park!