PONY λ M2 Modula-2

Pascal.CodeCompared.To/TypeScript

An interactive executable cheatsheet comparing Pascal and TypeScript

Free Pascal 3.2.2 TypeScript 6.0
Hello World & Running
Hello, World
program HelloWorld; begin writeln('Hello, World!'); end.
console.log("Hello, World!");
TypeScript (like JavaScript) runs a top-level script with no boilerplate. Pascal requires a program Name; header and an explicit begin...end. block with a trailing period. TypeScript uses console.log for standard output; Pascal uses the built-in writeln procedure. Single-quoted strings are standard Pascal; TypeScript uses double or single quotes interchangeably.
Comments
program CommentsDemo; begin // Single-line comment (Free Pascal extension) { Brace comment — traditional Pascal style } (* Parenthesis-star comment — also traditional *) writeln('done'); end.
// Single-line comment /* Multi-line block comment */ console.log("done");
Pascal offers three comment syntaxes. The brace style ({ }) is the most traditional; (* *) predates it; // is a Free Pascal extension from C. TypeScript uses the same // and /* */ as JavaScript. TypeScript also supports JSDoc comments (/** */) for IDE tooling and type inference from JavaScript.
Compile & Run
{ Compile with Free Pascal compiler: fpc HelloWorld.pas ./HelloWorld { on Linux/macOS } HelloWorld.exe { on Windows } Or use Compiler Explorer (godbolt.org) with the fpc322 compiler }
// Compile and run with tsx (no build step): // tsx script.ts // // Or compile then run: // tsc script.ts && node script.js // // Or use ts-node: // ts-node script.ts
Pascal compiles to a native binary — no runtime required on the target machine. TypeScript compiles to JavaScript, which then runs on Node.js or in a browser. The tsx tool (used by this site's test runner) runs TypeScript directly without a separate compile step, similar to how Free Pascal's fpc compiles and outputs a binary in one step.
Variables & Types
Variable Declaration
program VarDemo; var count: Integer; message: string; price: Double; active: Boolean; begin count := 10; message := 'hello'; price := 9.99; active := True; writeln(count, ' ', message, ' ', price:0:2, ' ', active); end.
let count: number = 10; let message: string = "hello"; let price: number = 9.99; let active: boolean = true; console.log(count, message, price.toFixed(2), active);
Pascal requires all variables to be declared in a var block before any code runs, with explicit types. TypeScript allows inline declarations anywhere using let (mutable) or const (immutable). Pascal uses := for assignment and = for comparison; TypeScript uses = for assignment and === for strict equality. Pascal's Boolean literals are True/False (capitalised); TypeScript uses lowercase true/false.
Type Inference
program TypeInference; var count: Integer; message: string; temperature: Double; begin { Pascal: every variable needs an explicit type } count := 42; message := 'explicit types required'; temperature := 36.6; writeln(count, ' ', message, ' ', temperature:0:1); end.
const count = 42; // inferred as number const message = "inferred"; // inferred as string const temperature = 36.6; // inferred as number console.log(count, message, temperature.toFixed(1));
TypeScript infers the type from the initialiser when you use const or let without an explicit annotation. The type is still fixed at compile time — TypeScript is statically typed, not dynamically typed. Pascal has no type inference; every variable requires an explicit type annotation in the var block. Prefer const over let in TypeScript when the value will not be reassigned.
Constants
program ConstantsDemo; const MaxItems = 100; Version = '3.2.2'; Gravity = 9.81; begin writeln(MaxItems); writeln(Version); writeln(Gravity:0:2); end.
const MAX_ITEMS = 100; // inferred as literal type 100 const VERSION = "6.0"; const GRAVITY = 9.81; console.log(MAX_ITEMS); console.log(VERSION); console.log(GRAVITY.toFixed(2));
Pascal groups constants in a const block before the main body; they are untyped and the compiler infers their type from the value. TypeScript's const keyword declares an immutable binding — the compiler infers a narrow literal type (100 rather than number). Both are truly immutable: any reassignment is a compile-time error.
Number Types
program NumberTypes; var small: ShortInt; { -128..127 } medium: Integer; { 32-bit signed } large: Int64; { 64-bit signed } fraction: Double; { 64-bit float } begin small := 127; medium := 2147483647; large := 9007199254740992; fraction := 3.14159; writeln(small); writeln(medium); writeln(large); writeln(fraction:0:5); end.
const small: number = 127; const medium: number = 2_147_483_647; // TypeScript has a single 'number' type (64-bit float) const large: number = 9_007_199_254_740_992; // MAX_SAFE_INTEGER const fraction: number = 3.14159; console.log(small, medium, large, fraction.toFixed(5)); // For true 64-bit integers use BigInt: const hugeInt = 9_223_372_036_854_775_807n; console.log(hugeInt);
Pascal provides fixed-width integer types (ShortInt, Integer, Int64, Cardinal). TypeScript has a single number type (IEEE 754 double-precision float), which can represent integers exactly only up to 253−1. For integers larger than that, TypeScript offers BigInt (suffixed with n). There is no overflow checking in either language's default integer arithmetic.
Boolean
program BooleanDemo; var active: Boolean; hasValue: Boolean; begin active := True; hasValue := False; writeln(active); writeln(not active); writeln(active and hasValue); writeln(active or hasValue); end.
const active: boolean = true; const hasValue: boolean = false; console.log(active); console.log(!active); console.log(active && hasValue); console.log(active || hasValue);
Pascal booleans are True/False (printed as TRUE/FALSE) and use English words for operators: and, or, not, xor. TypeScript uses lowercase true/false and C-style operators: &&, ||, !. TypeScript's && and || short-circuit; Pascal's and/or do not by default (use and then/or else in Free Pascal for short-circuit evaluation).
Type Conversion
program TypeConversion; uses SysUtils; var number: Integer; text: string; value: Double; begin number := 42; text := IntToStr(number); writeln(text + ' items'); writeln(StrToInt('99') * 2); value := 3.14; writeln(Trunc(value)); end.
const number = 42; const text = String(number); // or number.toString() console.log(text + " items"); console.log(Number("99") * 2); // or parseInt / parseFloat const value = 3.14; console.log(Math.trunc(value)); // truncate toward zero
Pascal uses conversion functions from SysUtils: IntToStr, StrToInt, Trunc. TypeScript provides global functions (Number(), String(), parseInt(), parseFloat()) and Math.trunc(). TypeScript will also coerce values implicitly in some contexts (e.g. string concatenation with + converts the number automatically), unlike Pascal which always requires explicit conversion.
Strings
Concatenation & Interpolation
program StringDemo; uses SysUtils; var name: string; age: Integer; begin name := 'Alice'; age := 30; writeln(name + ' is ' + IntToStr(age) + ' years old'); writeln(Format('%s is %d years old', [name, age])); end.
const name = "Alice"; const age = 30; console.log(name + " is " + age + " years old"); // concatenation console.log(`${name} is ${age} years old`); // template literal
Pascal uses + for concatenation but requires explicit numeric-to-string conversion (IntToStr). TypeScript's + automatically converts numbers when one operand is a string. TypeScript also supports template literals (backtick strings) with ${expression} interpolation — much more readable than Pascal's Format function for mixed-type strings.
String Methods
program StringMethods; uses SysUtils; var text: string; begin text := ' Hello, World! '; writeln(Trim(text)); writeln(UpperCase(text)); writeln(LowerCase(text)); writeln(Length(Trim(text))); writeln(Pos('World', text) > 0); end.
const text = " Hello, World! "; console.log(text.trim()); console.log(text.toUpperCase()); console.log(text.toLowerCase()); console.log(text.trim().length); console.log(text.includes("World"));
Pascal string functions are free-standing procedures from SysUtils (Trim(s), UpperCase(s)); TypeScript strings are objects with methods called via dot notation (s.trim(), s.toUpperCase()). TypeScript strings are immutable — all methods return new strings. Pascal strings can be mutated in place using Insert, Delete, and direct character assignment.
Search & Slice
program StringSearch; var text: string; position: Integer; begin text := 'Hello, World!'; position := Pos('World', text); writeln(position); { 8 — 1-indexed } writeln(position > 0); { TRUE if found } writeln(Copy(text, 1, 5)); { 'Hello' } writeln(Copy(text, 8, 6)); { 'World!' } end.
const text = "Hello, World!"; const position = text.indexOf("World"); console.log(position); // 7 — 0-indexed console.log(position >= 0); // true if found console.log(text.includes("World")); // true — cleaner check console.log(text.slice(0, 5)); // "Hello" console.log(text.slice(7)); // "World!"
Pascal's Pos returns a 1-based position (0 if not found); TypeScript's indexOf returns a 0-based position (-1 if not found). Pascal's Copy(str, start, length) uses 1-based indexing; TypeScript's slice(start, end) uses 0-based indices with an optional end (exclusive). TypeScript's includes is cleaner than checking indexOf >= 0 for simple membership tests.
Split & Join
{$H+} program StringSplit; uses SysUtils, StrUtils, Types; var sentence: string; words: TStringDynArray; index: Integer; begin sentence := 'one two three'; words := SplitString(sentence, ' '); for index := 0 to Length(words) - 1 do writeln(words[index]); writeln(words[0] + '-' + words[1] + '-' + words[2]); end.
const sentence = "one two three"; const words = sentence.split(" "); words.forEach(word => console.log(word)); console.log(words.join("-"));
Pascal's SplitString (from StrUtils) returns a zero-indexed TStringDynArray. TypeScript's split returns a native string[] that integrates directly with array methods. TypeScript's join re-combines the array with a separator — Pascal requires a manual loop for the equivalent.
Arrays & Collections
Array Literals
program ArrayDemo; var numbers: array[1..5] of Integer; index: Integer; begin numbers[1] := 10; numbers[2] := 20; numbers[3] := 30; numbers[4] := 40; numbers[5] := 50; writeln(Length(numbers)); writeln(numbers[3]); for index := 1 to Length(numbers) do writeln(numbers[index]); end.
const numbers: number[] = [10, 20, 30, 40, 50]; console.log(numbers.length); console.log(numbers[2]); // 0-indexed for (const number of numbers) console.log(number);
Pascal fixed arrays use a 1-based index by default (array[1..5]), declared and assigned separately. TypeScript arrays are always 0-based, and can be declared and initialised with an array literal in one line. TypeScript arrays are objects with rich methods (push, pop, map, filter, reduce); Pascal arrays are plain memory regions with no methods.
Dynamic Arrays
program DynamicArray; var items: array of string; begin SetLength(items, 3); items[0] := 'first'; items[1] := 'second'; items[2] := 'third'; SetLength(items, 4); items[3] := 'fourth'; writeln(Length(items)); writeln(items[2]); end.
const items: string[] = ["first", "second", "third"]; items.push("fourth"); console.log(items.length); console.log(items[2]); items.pop(); console.log(items.length);
Pascal's dynamic array (array of T) is resized with SetLength. TypeScript arrays grow automatically with push, pop, shift, unshift, and splice. There is no SetLength equivalent — the array manages its own memory. TypeScript arrays are backed by JavaScript's dynamic array model, not a fixed-size block of typed memory.
Array Methods
program ArrayMethods; var numbers: array[1..5] of Integer; total: Integer; largest: Integer; index: Integer; begin numbers[1] := 3; numbers[2] := 1; numbers[3] := 4; numbers[4] := 1; numbers[5] := 5; total := 0; largest := numbers[1]; for index := 1 to 5 do begin total := total + numbers[index]; if numbers[index] > largest then largest := numbers[index]; end; writeln('Sum: ', total); writeln('Max: ', largest); end.
const numbers = [3, 1, 4, 1, 5]; const total = numbers.reduce((sum, n) => sum + n, 0); const largest = Math.max(...numbers); console.log("Sum:", total); console.log("Max:", largest); console.log("Sorted:", [...numbers].sort((a, b) => a - b));
TypeScript arrays have built-in functional methods — reduce, map, filter, find, some, every — that replace manual loops. Pascal has none of these; every aggregation requires an explicit loop. The spread operator (...) in TypeScript copies an array before sorting, since sort mutates in place.
Tuples
program TupleDemo; type TCoordinate = record X: Double; Y: Double; end; var origin: TCoordinate; point: TCoordinate; begin origin.X := 0.0; origin.Y := 0.0; point.X := 3.0; point.Y := 4.0; writeln(origin.X:0:1, ', ', origin.Y:0:1); writeln(point.X:0:1, ', ', point.Y:0:1); end.
const origin: [number, number] = [0.0, 0.0]; const point: [number, number] = [3.0, 4.0]; console.log(origin); console.log(point); // Named tuple elements (TypeScript 4+): type Coordinate = [x: number, y: number]; const named: Coordinate = [3.0, 4.0]; console.log(named[0], named[1]);
Pascal uses records to group heterogeneous data. TypeScript has native tuple types: fixed-length arrays where each position has a specific type. TypeScript 4 added named tuple elements ([x: number, y: number]) for documentation purposes. Tuples are useful for multiple return values — a pattern that in Pascal requires either a record or var parameters.
Map (Key-Value)
program MapDemo; type TEntry = record key: string; value: Integer; end; var scores: array[1..3] of TEntry; index: Integer; begin scores[1].key := 'Alice'; scores[1].value := 95; scores[2].key := 'Bob'; scores[2].value := 87; scores[3].key := 'Carol'; scores[3].value := 92; for index := 1 to 3 do writeln(scores[index].key, ': ', scores[index].value); end.
const scores = new Map<string, number>([ ["Alice", 95], ["Bob", 87], ["Carol", 92], ]); console.log(scores.get("Alice")); // 95 scores.set("Dave", 78); for (const [name, score] of scores) console.log(`${name}: ${score}`);
Standard Pascal has no built-in key-value map. TypeScript's Map<K, V> provides O(1) lookups, iteration in insertion order, and a clean API. Plain TypeScript objects ({ Alice: 95, Bob: 87 }) also serve as key-value stores for string keys. Map is preferred when keys are not known at compile time or when iteration order matters.
Control Flow
If / Else
program IfElse; var score: Integer; begin score := 85; if score >= 90 then writeln('A') else if score >= 80 then writeln('B') else if score >= 70 then writeln('C') else writeln('Below C'); end.
const score = 85; if (score >= 90) console.log("A"); else if (score >= 80) console.log("B"); else if (score >= 70) console.log("C"); else console.log("Below C");
The if/else structure is nearly identical. Pascal requires then after the condition; TypeScript requires parentheses around the condition and uses curly braces for multi-statement bodies. Pascal places a semicolon before else only in specific cases — a common source of Pascal syntax errors. TypeScript's optional braces follow the same rules as C.
Case / Switch
program CaseDemo; var dayNumber: Integer; begin dayNumber := 3; case dayNumber of 1: writeln('Monday'); 2: writeln('Tuesday'); 3: writeln('Wednesday'); 4, 5: writeln('Thursday or Friday'); 6, 7: writeln('Weekend'); else writeln('Unknown'); end; end.
const dayNumber = 3; switch (dayNumber) { case 1: console.log("Monday"); break; case 2: console.log("Tuesday"); break; case 3: console.log("Wednesday"); break; case 4: case 5: console.log("Thursday or Friday"); break; case 6: case 7: console.log("Weekend"); break; default: console.log("Unknown"); }
Pascal's case...of never falls through between branches. TypeScript's switch does fall through unless you add break — a common source of bugs. Multiple values share a branch in Pascal with comma syntax (4, 5:); TypeScript uses stacked case labels with fall-through. For a more Pascal-like no-fall-through construct, TypeScript developers often use if/else chains or object lookups instead.
Ternary / Conditional Expression
program TernaryDemo; var score: Integer; result: string; begin score := 75; if score >= 60 then result := 'pass' else result := 'fail'; writeln(result); end.
const score = 75; const result = score >= 60 ? "pass" : "fail"; console.log(result);
Pascal has no ternary expression — an if/else statement must be used to conditionally assign a variable. TypeScript inherits JavaScript's condition ? thenValue : elseValue ternary operator, which is an expression that can appear anywhere a value is expected. For more complex conditions TypeScript also has the nullish coalescing operator (value ?? default), which Pascal has no equivalent for.
Loops
Numeric For Loop
program ForLoops; var counter: Integer; begin for counter := 1 to 5 do writeln(counter); for counter := 5 downto 1 do writeln(counter); end.
for (let counter = 1; counter <= 5; counter++) console.log(counter); for (let counter = 5; counter >= 1; counter--) console.log(counter);
Pascal's for counter := 1 to 5 do is always step-by-1; downto is step-by-(-1). There is no variable step size. TypeScript's C-style for loop is more flexible: any initialiser, condition, and increment are valid. TypeScript also provides for...of for iterating values and for...in for iterating keys.
For-Each / For-Of
program ForeachDemo; var fruits: array[1..4] of string; index: Integer; begin fruits[1] := 'apple'; fruits[2] := 'banana'; fruits[3] := 'cherry'; fruits[4] := 'date'; { Pascal has no foreach; use an indexed for loop } for index := 1 to Length(fruits) do writeln(fruits[index]); end.
const fruits = ["apple", "banana", "cherry", "date"]; for (const fruit of fruits) console.log(fruit); // With index using entries(): for (const [index, fruit] of fruits.entries()) console.log(`${index + 1}: ${fruit}`);
Pascal has no foreach construct — iterating a collection always uses a numeric for loop with an explicit index variable. TypeScript's for...of iterates over the values of any iterable (arrays, strings, Sets, Maps, generators) without needing an index. Array.entries() provides both the index and value when you need both.
While & Repeat-Until
program WhileRepeat; var count: Integer; begin count := 1; while count <= 3 do begin writeln('while: ', count); Inc(count); end; count := 0; repeat Inc(count); writeln('repeat: ', count); until count >= 3; end.
let count = 1; while (count <= 3) { console.log("while:", count); count++; } count = 0; do { count++; console.log("do-while:", count); } while (count < 3);
Pascal's while maps directly to TypeScript's while. Pascal's repeat...until condition (exit when true) maps to TypeScript's do...while (condition) (exit when false) — the exit condition is the logical inverse. Pascal's built-in Inc(count) procedure increments a variable; TypeScript uses the count++ or count += 1 operators.
Functions & Procedures
Procedure (Void Function)
program ProcedureDemo; procedure Greet(name: string); begin writeln('Hello, ', name, '!'); end; procedure PrintSeparator(character: Char; count: Integer); var index: Integer; begin for index := 1 to count do write(character); writeln; end; begin Greet('World'); PrintSeparator('-', 20); Greet('Pascal'); end.
function greet(name: string): void { console.log(`Hello, ${name}!`); } function printSeparator(character: string, count: number): void { console.log(character.repeat(count)); } greet("World"); printSeparator("-", 20); greet("TypeScript");
Pascal distinguishes procedures (no return value) from functions (with a return value) as separate keywords. TypeScript uses a single function keyword and marks no-return-value functions with the void return type. The : void annotation is optional but recommended in TypeScript for clarity. TypeScript's string.repeat(n) replaces Pascal's manual character-printing loop.
Function (Return Value)
program FunctionDemo; uses SysUtils; function Square(number: Integer): Integer; begin Result := number * number; end; function Greet(name: string): string; begin Result := 'Hello, ' + name + '!'; end; begin writeln(Square(7)); writeln(Greet('World')); writeln(Square(Square(2))); end.
function square(number: number): number { return number * number; } function greet(name: string): string { return `Hello, ${name}!`; } console.log(square(7)); console.log(greet("World")); console.log(square(square(2)));
Pascal functions use Result := value to set the return value; the function body can contain any number of assignments to Result before returning. TypeScript uses return value which exits the function immediately. Both languages require the return type to be declared in the function signature.
Arrow Functions / Lambdas
program FunctionTypes; type TIntOp = function(value: Integer): Integer; function Double(value: Integer): Integer; begin Result := value * 2; end; function ApplyTwice(operation: TIntOp; value: Integer): Integer; begin Result := operation(operation(value)); end; begin writeln(ApplyTwice(@Double, 3)); { 12 } end.
const double = (value: number): number => value * 2; const square = (value: number): number => value * value; function applyTwice(operation: (n: number) => number, value: number) { return operation(operation(value)); } console.log(applyTwice(double, 3)); // 12 console.log(applyTwice(square, 3)); // 81
Pascal supports passing functions as values using typed function pointers, but they must be named functions referenced with the @ operator. TypeScript arrow functions ((param) => expression) define anonymous functions inline. TypeScript arrow functions are also closures — they capture variables from the surrounding scope. Function types in TypeScript are written as (param: Type) => ReturnType.
Default & Optional Parameters
program DefaultParams; procedure Log(message: string; prefix: string = 'INFO'); begin writeln('[' + prefix + '] ' + message); end; begin Log('Server started'); Log('Something failed', 'ERROR'); end.
function log(message: string, prefix = "INFO"): void { console.log(`[${prefix}] ${message}`); } // Optional parameter (may be omitted): function greet(name: string, greeting?: string): string { return `${greeting ?? "Hello"}, ${name}!`; } log("Server started"); log("Something failed", "ERROR"); console.log(greet("Alice")); console.log(greet("Bob", "Good day"));
Both languages support default parameter values with identical syntax. TypeScript additionally has optional parameters marked with ? — they can be omitted entirely and arrive as undefined inside the function. The nullish coalescing operator (??) provides a default for undefined or null values. Pascal has no optional parameters; every parameter must be supplied.
Rest / Variadic Parameters
program VariadicDemo; uses SysUtils; { Pascal has no built-in variadic parameter support. The closest is an open array parameter: } function SumAll(const values: array of Integer): Integer; var index: Integer; begin Result := 0; for index := 0 to Length(values) - 1 do Result := Result + values[index]; end; begin writeln(SumAll([1, 2, 3])); writeln(SumAll([10, 20, 30, 40])); end.
function sumAll(...values: number[]): number { return values.reduce((total, value) => total + value, 0); } console.log(sumAll(1, 2, 3)); // 6 console.log(sumAll(10, 20, 30, 40)); // 100
Pascal's open array parameter (const values: array of T) accepts an array of any size and is the closest equivalent to variadic parameters — but callers must pass an explicit array literal. TypeScript's rest parameter (...values: T[]) lets callers pass any number of individual arguments, which are collected into an array inside the function.
Classes & OOP
Class Definition
program ClassDemo; type TAnimal = class private FName: string; public constructor Create(aName: string); function Speak: string; property Name: string read FName; end; constructor TAnimal.Create(aName: string); begin FName := aName; end; function TAnimal.Speak: string; begin Result := FName + ' makes a sound'; end; var animal: TAnimal; begin animal := TAnimal.Create('Dog'); writeln(animal.Speak); writeln(animal.Name); animal.Free; end.
class Animal { readonly name: string; constructor(name: string) { this.name = name; } speak(): string { return `${this.name} makes a sound`; } } const animal = new Animal("Dog"); console.log(animal.speak()); console.log(animal.name);
Pascal class bodies separate declaration (in the type block) from implementation (method bodies written below). TypeScript classes are self-contained. Pascal requires manual Free to destroy objects; TypeScript uses garbage collection. Pascal private fields use the F prefix by convention (FName); TypeScript uses the private keyword or the #name private field syntax.
Inheritance
program InheritanceDemo; type TShape = class public function Area: Double; virtual; abstract; procedure Describe; end; TCircle = class(TShape) private FRadius: Double; public constructor Create(radius: Double); function Area: Double; override; end; procedure TShape.Describe; begin writeln('Area = ', Area:0:4); end; constructor TCircle.Create(radius: Double); begin FRadius := radius; end; function TCircle.Area: Double; begin Result := 3.14159265 * FRadius * FRadius; end; var circle: TCircle; begin circle := TCircle.Create(5.0); circle.Describe; circle.Free; end.
abstract class Shape { abstract area(): number; describe(): void { console.log(`Area = ${this.area().toFixed(4)}`); } } class Circle extends Shape { constructor(private radius: number) { super(); } area(): number { return Math.PI * this.radius ** 2; } } const circle = new Circle(5.0); circle.describe();
Inheritance syntax differs in key ways. Pascal uses class(ParentClass) for the base class and the virtual; abstract; directive pair; overrides require override. TypeScript uses extends, the abstract keyword on both the class and the method, and override is optional (recommended with noImplicitOverride). TypeScript's constructor parameter shorthand (private radius: number) declares and assigns the field in one step.
Access Modifiers
program AccessDemo; type TBankAccount = class private FBalance: Double; protected procedure SetBalance(amount: Double); public constructor Create(initialBalance: Double); function GetBalance: Double; procedure Deposit(amount: Double); end; constructor TBankAccount.Create(initialBalance: Double); begin FBalance := initialBalance; end; procedure TBankAccount.SetBalance(amount: Double); begin FBalance := amount; end; function TBankAccount.GetBalance: Double; begin Result := FBalance; end; procedure TBankAccount.Deposit(amount: Double); begin FBalance := FBalance + amount; end; var account: TBankAccount; begin account := TBankAccount.Create(1000.0); account.Deposit(250.0); writeln(account.GetBalance:0:2); account.Free; end.
class BankAccount { private balance: number; constructor(initialBalance: number) { this.balance = initialBalance; } protected setBalance(amount: number): void { this.balance = amount; } getBalance(): number { return this.balance; } deposit(amount: number): void { this.balance += amount; } } const account = new BankAccount(1000); account.deposit(250); console.log(account.getBalance().toFixed(2));
Both languages have private, protected, and public access modifiers with identical semantics. Pascal access modifiers are section headers inside the class declaration (private followed by members); TypeScript uses per-member keywords. TypeScript also has readonly and #field (truly private at runtime, unlike private which is only a compile-time check).
Interfaces & Type Aliases
Interface
program InterfaceDemo; type IGreeter = interface function Greet(name: string): string; procedure SetLanguage(lang: string); end; TEnglishGreeter = class(TInterfacedObject, IGreeter) public function Greet(name: string): string; procedure SetLanguage(lang: string); end; function TEnglishGreeter.Greet(name: string): string; begin Result := 'Hello, ' + name + '!'; end; procedure TEnglishGreeter.SetLanguage(lang: string); begin writeln('Language set to: ', lang); end; var greeter: IGreeter; begin greeter := TEnglishGreeter.Create; writeln(greeter.Greet('Alice')); greeter.SetLanguage('English'); end.
interface Greeter { greet(name: string): string; setLanguage(lang: string): void; } class EnglishGreeter implements Greeter { greet(name: string): string { return `Hello, ${name}!`; } setLanguage(lang: string): void { console.log(`Language set to: ${lang}`); } } const greeter: Greeter = new EnglishGreeter(); console.log(greeter.greet("Alice")); greeter.setLanguage("English");
Interfaces work similarly in both languages. Pascal requires class implementations to inherit from TInterfacedObject; TypeScript uses the implements keyword. A key difference: TypeScript interfaces support structural typing — any object with the required shape satisfies the interface, even without an explicit implements declaration. Pascal uses nominal typing — the explicit declaration is required.
Type Aliases & Object Types
program TypeAliasDemo; type Score = Integer; { type alias } TPoint = record { structured type } X, Y: Double; end; TName = string; var points: Score; location: TPoint; firstName: TName; begin points := 95; location.X := 3.0; location.Y := 4.0; firstName := 'Alice'; writeln(points); writeln(location.X:0:1, ', ', location.Y:0:1); writeln(firstName); end.
type Score = number; // type alias type Name = string; // type alias type Point = { x: number; y: number }; // object type alias const points: Score = 95; const location: Point = { x: 3.0, y: 4.0 }; const firstName: Name = "Alice"; console.log(points); console.log(`${location.x}, ${location.y}`); console.log(firstName);
Pascal's type block creates named type aliases and new structured types (record). TypeScript's type keyword creates aliases for any type — primitives, objects, unions, intersections, or tuples. TypeScript object types ({ x: number; y: number }) are structurally typed: any object with those properties satisfies the type, regardless of how it was created.
Union Types
program UnionDemo; type { Pascal uses variant records for discriminated unions } TValueKind = (VKInteger, VKString, VKBoolean); TValue = record Kind: TValueKind; IntVal: Integer; StrVal: string; BoolVal: Boolean; end; var value: TValue; begin value.Kind := VKString; value.StrVal := 'hello'; case value.Kind of VKInteger: writeln(value.IntVal); VKString: writeln(value.StrVal); VKBoolean: writeln(value.BoolVal); end; end.
type Value = number | string | boolean; function describe(value: Value): void { if (typeof value === "number") console.log("number:", value); else if (typeof value === "string") console.log("string:", value); else console.log("boolean:", value); } describe(42); describe("hello"); describe(true);
Pascal uses variant records (a record with a discriminant tag and overlapping fields) to represent values of multiple types. TypeScript has first-class union types (number | string | boolean) which are checked with typeof guards. TypeScript discriminated unions (type Shape = { kind: "circle", radius: number } | { kind: "square", side: number }) are the idiomatic equivalent of Pascal's variant records — and much cleaner to use.
Generics
program GenericsDemo; type generic TBox<T> = class private FValue: T; public constructor Create(value: T); function GetValue: T; end; constructor TBox.Create(value: T); begin FValue := value; end; function TBox.GetValue: T; begin Result := FValue; end; var numberBox: specialize TBox<Integer>; wordBox: specialize TBox<string>; begin numberBox := specialize TBox<Integer>.Create(42); wordBox := specialize TBox<string>.Create('hello'); writeln(numberBox.GetValue); writeln(wordBox.GetValue); numberBox.Free; wordBox.Free; end.
class Box<T> { constructor(private value: T) {} getValue(): T { return this.value; } } const numberBox = new Box<number>(42); const wordBox = new Box<string>("hello"); console.log(numberBox.getValue()); console.log(wordBox.getValue()); // Generic function: function identity<T>(value: T): T { return value; } console.log(identity(100)); console.log(identity("world"));
Both languages support generics with type parameters. Free Pascal uses generic and specialize keywords; TypeScript uses angle-bracket syntax consistently. TypeScript generics are erased at runtime (like Java), while Pascal generics generate separate compiled code per type (like C++ templates). TypeScript generic constraints use extends: function largest<T extends { length: number }>(items: T[]).
Error Handling
Try / Except (Catch)
program TryExcept; uses SysUtils; var result: Integer; begin try result := StrToInt('not a number'); writeln(result); except on error: EConvertError do writeln('Conversion error: ', error.Message); on error: Exception do writeln('Unknown error: ', error.Message); end; end.
try { const result = parseInt("not a number", 10); if (isNaN(result)) throw new TypeError("Invalid number"); console.log(result); } catch (error) { if (error instanceof TypeError) console.log("Type error:", (error as TypeError).message); else console.log("Unknown error:", error); }
Both languages use try/catch (Pascal calls it try/except). Pascal supports typed catches with on ErrorVar: ExceptionClass do; TypeScript's catch receives an unknown type that must be narrowed with instanceof. Note that TypeScript's parseInt does not throw on invalid input — it returns NaN — so explicit validation is often needed, unlike Pascal's StrToInt which throws EConvertError.
Custom Error Types
program CustomException; uses SysUtils; type EValidationError = class(Exception) private FFieldName: string; public constructor Create(fieldName, messageText: string); property FieldName: string read FFieldName; end; constructor EValidationError.Create(fieldName, messageText: string); begin inherited Create(messageText); FFieldName := fieldName; end; begin try raise EValidationError.Create('email', 'Invalid format'); except on error: EValidationError do writeln('Field "', error.FieldName, '": ', error.Message); end; end.
class ValidationError extends Error { constructor( public readonly fieldName: string, message: string, ) { super(message); this.name = "ValidationError"; } } try { throw new ValidationError("email", "Invalid format"); } catch (error) { if (error instanceof ValidationError) console.log(`Field "${error.fieldName}": ${error.message}`); }
Custom exceptions follow the same pattern in both languages: extend the base exception class, add properties, and call the parent constructor. Pascal's convention names exception classes with an E prefix (EValidationError); TypeScript's convention uses an Error suffix (ValidationError). Setting this.name in TypeScript is recommended so error.name identifies the type correctly in logs.
Finally Block
program TryFinally; uses SysUtils; var resource: string; begin resource := 'open'; try try writeln('Using resource'); raise Exception.Create('Something went wrong'); finally resource := 'closed'; writeln('Resource: ', resource); end; except on error: Exception do writeln('Caught: ', error.Message); end; end.
let resource = "open"; try { console.log("Using resource"); throw new Error("Something went wrong"); } catch (error) { console.log("Caught:", (error as Error).message); } finally { resource = "closed"; console.log("Resource:", resource); }
Both languages support finally blocks that execute regardless of whether an exception was raised. Pascal's try...finally is structurally separate from try...except — to both handle an exception and clean up, you must nest them or use try...except...finally (Free Pascal supports this). TypeScript combines catch and finally in a single try block naturally.
Modules
Importing from a Module
program ModuleDemo; uses SysUtils, { string conversion, date/time } Math, { mathematical functions } StrUtils; { string utilities } begin writeln(Format('Pi = %.4f', [Pi])); writeln(Max(10, 20)); writeln(UpperCase('hello')); end.
// Named imports: import { readFileSync } from "fs"; import { join } from "path"; // Default import: // import lodash from "lodash"; // Namespace import: // import * as math from "mathjs"; // (This example uses node builtins — norun in browser) console.log(join("/tmp", "file.txt"));
Pascal's uses clause imports entire units; all their exported identifiers become available in the current scope. TypeScript uses ES module syntax: import { name } from "module" for named exports, import Default from "module" for a default export, and import * as ns from "module" to import everything under a namespace. TypeScript modules are files — each file is its own module.
Exporting from a Module
{ Pascal unit structure: unit MathUtils; interface { public declarations } function Square(n: Integer): Integer; function Cube(n: Integer): Integer; implementation { private implementation } function Square(n: Integer): Integer; begin Result := n * n; end; function Cube(n: Integer): Integer; begin Result := n * n * n; end; end. }
// mathUtils.ts export function square(n: number): number { return n * n; } export function cube(n: number): number { return n * n * n; } export const PI = 3.14159265; // Consumer: // import { square, cube } from "./mathUtils";
Pascal units have a strict interface/implementation split: the interface section declares what is public; the implementation section provides the bodies (and can also contain private helpers not in the interface). TypeScript exports individual declarations with the export keyword. Types, functions, classes, constants, and variables can all be exported; anything without export is private to the file.