TypeScript für Delphi-Entwickler: Die Grundlagen verstehen (Teil 2 von 5)¶
Willkommen zurück¶
In Teil 1 haben wir besprochen, warum der Übergang von Delphi zu Next.js geschäftlich Sinn macht. Jetzt erkunden wir TypeScript selbst und warum es sich überraschend vertraut für Delphi-Entwickler anfühlt.
Denken Sie daran: Diese Serie handelt nicht davon, Ihnen beizubringen, TypeScript zu programmieren. Es geht darum, Ihnen zu zeigen, dass die Konzepte, die Sie in Delphi gemeistert haben, direkt übertragbar sind, und dass die Lernkurve kürzer ist, als Sie befürchten mögen.
Das Type System: Delphi-DNA in JavaScript¶
Basic Types - Die Grundlagen¶
In Delphi deklarieren Sie Typen explizit:
TypeScript ist nahezu identisch:
Hinweis: TypeScript verwendet number für alle numerischen Types (Integer, Double, Single, etc.). JavaScript unterscheidet intern nicht zwischen Integer- und Floating-Point-Zahlen, aber TypeScripts Type Checking stellt Type Safety sicher.
Type Inference - Der intelligente Compiler¶
Erinnern Sie sich, wie modernes Delphi Types ableiten kann?
TypeScript macht das wunderbar:
Anders brachte diese Weisheit mit. Der Compiler ist intelligent genug, um Types herauszufinden, aber Sie können immer noch explizit sein, wenn Sie Klarheit wollen.
Interfaces - Ihr Alter Freund¶
Interfaces in Delphi definieren Verträge:
type
ICustomer = interface
function GetId: Integer;
function GetName: string;
procedure SetName(const Value: string);
property Id: Integer read GetId;
property Name: string read GetName write SetName;
end;
TypeScript Interfaces sind sauberer und gleichermaßen mächtig:
interface Customer {
id: number;
name: string;
email: string;
readonly createdAt: Date; // readonly wie "read" in Delphi
phoneNumber?: string; // ? bedeutet optional
}
Verwendung des Interface:
function saveCustomer(customer: Customer): void {
console.log(`Saving ${customer.name}`);
// customer.createdAt = new Date(); // ERROR! readonly property
}
const newCustomer: Customer = {
id: 1,
name: "John Smith",
email: "john@example.com",
createdAt: new Date()
// phoneNumber is optional, so we can omit it
};
saveCustomer(newCustomer); // Type-safe!
Während das Delphi Interface eine implementierte Klasse erfordern würde, stellen TypeScript Interfaces sicher, dass Objekte der erwarteten Form entsprechen, ohne eine explizite Implementierung zu benötigen. Ich lasse die Class-Implementierung hier der Kürze halber weg.
Optionale und Nullable Types¶
In Delphi könnten Sie verwenden:
TypeScript hat eingebaute Nullable Types:
let optionalValue: number | null = null; // Kann number oder null sein
let undefinedValue: number | undefined; // Kann number oder undefined sein
let maybeString: string | null | undefined;
// Type Guard (wie Assigned() Check in Delphi)
if (optionalValue !== null) {
console.log(optionalValue * 2); // Sicher zu verwenden
}
Der ? Operator macht das noch sauberer:
interface Address {
street: string;
city: string;
zipCode?: string; // Optional property
}
function displayAddress(address: Address): void {
console.log(address.street);
console.log(address.zipCode?.toUpperCase()); // Safe Navigation
}
Das ist wie Delphis Assigned() Check, aber in die Sprach-Syntax eingebaut.
Klassen - Object-Oriented Programming lebt weiter¶
Sie kennen Klassen. TypeScript hat sie auch:
Delphi Class:¶
type
TCustomer = class
private
FId: Integer;
FName: string;
FBalance: Double;
public
constructor Create(AId: Integer; const AName: string);
destructor Destroy; override;
procedure AddToBalance(Amount: Double);
property Id: Integer read FId;
property Name: string read FName write FName;
property Balance: Double read FBalance;
end;
constructor TCustomer.Create(AId: Integer; const AName: string);
begin
inherited Create;
FId := AId;
FName := AName;
FBalance := 0.0;
end;
procedure TCustomer.AddToBalance(Amount: Double);
begin
FBalance := FBalance + Amount;
end;
TypeScript Class:¶
class Customer {
private id: number;
private balance: number;
public name: string;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
this.balance = 0;
}
addToBalance(amount: number): void {
this.balance += amount;
}
getBalance(): number {
return this.balance;
}
// Getter Property (wie Delphi Property read)
get customerId(): number {
return this.id;
}
}
// Verwendung
const customer = new Customer(1, "Jane Doe");
customer.addToBalance(1000);
console.log(customer.getBalance()); // 1000
console.log(customer.customerId); // 1 (using getter)
Moderne TypeScript Kurzschreibweise¶
TypeScript hat eine Abkürzung, die Delphi-Entwickler schätzen werden:
class Product {
constructor(
private id: number,
public name: string,
public price: number,
readonly category: string
) {
// Constructor Body kann leer sein!
// Properties werden automatisch erstellt und zugewiesen
}
displayInfo(): void {
console.log(`${this.name}: $${this.price}`);
}
}
const product = new Product(1, "Widget", 29.99, "Hardware");
product.displayInfo(); // Widget: $29.99
Das ist unglaublich prägnant und behält dabei Type Safety bei.
Generics - Type-Safe Collections¶
Delphi Generics:
type
TList<T> = class
private
FItems: array of T;
public
procedure Add(Item: T);
function Get(Index: Integer): T;
end;
var
CustomerList: TList<TCustomer>;
NumberList: TList<Integer>;
TypeScript Generics:
// Generic Function
function firstElement<T>(arr: T[]): T | undefined {
return arr[0];
}
const firstNumber = firstElement([1, 2, 3]); // Type: number | undefined
const firstName = firstElement(["a", "b"]); // Type: string | undefined
// Generic Class
class DataStore<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
get(index: number): T | undefined {
return this.items[index];
}
getAll(): T[] {
return [...this.items]; // Gibt eine Kopie zurück
}
}
// Verwendung
const customerStore = new DataStore<Customer>();
customerStore.add(newCustomer);
const numberStore = new DataStore<number>();
numberStore.add(42);
Der Compiler stellt Type Safety durchgängig sicher. Versuchen Sie, einen String zu numberStore hinzuzufügen, und TypeScript wird Sie zur Compile Time stoppen.
Enums - Named Constants Richtig Gemacht¶
Delphi Enums:
type
TOrderStatus = (osNew, osPending, osShipped, osDelivered, osCancelled);
var
Status: TOrderStatus;
begin
Status := osShipped;
end;
TypeScript Enums:
enum OrderStatus {
New = "NEW",
Pending = "PENDING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED"
}
let status: OrderStatus = OrderStatus.Shipped;
function updateOrder(orderId: number, status: OrderStatus): void {
console.log(`Order ${orderId} is now ${status}`);
}
updateOrder(123, OrderStatus.Delivered);
// updateOrder(123, "DELIVERED"); // ERROR! Type Safety durchgesetzt
Const Enums (Performance-Optimierung)¶
const enum Direction {
Up,
Down,
Left,
Right
}
let move = Direction.Up; // Kompiliert zu: let move = 0 (kein Runtime Overhead)
Type Unions und Intersections - Mächtiger als Delphi¶
TypeScript kann Dinge, die Delphi nicht einfach kann:
Union Types (OR-Logik)¶
type Status = "active" | "inactive" | "pending";
type ID = number | string;
function getUserStatus(id: ID): Status {
// id kann number ODER string sein
return "active";
}
getUserStatus(123); // OK
getUserStatus("user-456"); // OK
getUserStatus(true); // ERROR!
Intersection Types (UND-Logik)¶
interface Timestamped {
createdAt: Date;
updatedAt: Date;
}
interface Identifiable {
id: number;
}
type Entity = Timestamped & Identifiable;
const user: Entity = {
id: 1,
createdAt: new Date(),
updatedAt: new Date()
// Muss ALLE Properties von beiden Interfaces haben
};
Functions - First-Class Citizens¶
In Delphi deklarieren Sie Funktionen so:
function CalculateTotal(Price: Double; Quantity: Integer): Double;
begin
Result := Price * Quantity;
end;
TypeScript Functions sind flexibler:
// Traditionelle Function
function calculateTotal(price: number, quantity: number): number {
return price * quantity;
}
// Arrow Function (Lambda Expression)
const calculateTotal = (price: number, quantity: number): number => {
return price * quantity;
};
// Prägnante Arrow Function (impliziter Return)
const calculateTotal = (price: number, quantity: number): number =>
price * quantity;
// Optionale Parameter
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : name;
}
greet("Smith"); // "Smith"
greet("Smith", "Dr."); // "Dr. Smith"
// Default Parameter
function createUser(name: string, role: string = "user"): void {
console.log(`Creating ${role}: ${name}`);
}
createUser("John"); // "Creating user: John"
createUser("Jane", "admin"); // "Creating admin: Jane"
Type Aliases für Functions¶
type MathOperation = (a: number, b: number) => number;
const add: MathOperation = (a, b) => a + b;
const multiply: MathOperation = (a, b) => a * b;
function calculate(op: MathOperation, x: number, y: number): number {
return op(x, y);
}
console.log(calculate(add, 5, 3)); // 8
console.log(calculate(multiply, 5, 3)); // 15
Das ist ähnlich zu Delphis Procedural Types, aber eleganter.
Arrays und Collections: Ein anderer Ansatz¶
In Delphi arbeiten Sie mit Arrays so:
var
Numbers: TArray<Integer>;
begin
SetLength(Numbers, 3);
Numbers[0] := 1;
Numbers[1] := 2;
Numbers[2] := 3;
end;
TypeScript Arrays sind einfacher und mächtiger:
Aber hier wird es interessant. TypeScript umarmt einen Functional Programming Stil, der sich anfangs ungewohnt anfühlen könnte:
// Anstatt einer for-Schleife verwenden Sie oft map, filter, reduce
const doubled = numbers.map(n => n * 2); // [2, 4, 6]
const evens = numbers.filter(n => n % 2 === 0); // [2]
const sum = numbers.reduce((acc, n) => acc + n, 0); // 6
Dieser Stil ist prägnanter, sobald Sie sich daran gewöhnt haben. Denken Sie daran als deklarative statt imperative Programmierung. Anstatt dem Computer zu sagen, wie er die Schleife durchlaufen soll, sagen Sie ihm, was Sie mit jedem Element machen wollen.
Was das alles für Sie bedeutet¶
Sie haben jetzt die Kernkonzepte von TypeScript gesehen und wie sie sich auf Delphi abbilden:
- Typen sind vertraut:
Integerwird zunumber,stringbleibtstring,Booleanwird zuboolean - Interfaces funktionieren gleich: Verträge für Ihre Datenstrukturen definieren
- Klassen existieren immer noch: Object-Oriented Programming ist nicht verschwunden
- Generics sind da: Type-safe Collections und Functions
- Enums bieten Named Constants: Genau wie in Delphi
Die Syntax ist anders, aber die Konzepte sind identisch. Das ist kein Zufall—Anders Hejlsberg entwarf beide Sprachen mit derselben Philosophie: Entwicklern mächtige Tools geben, die Fehler früh abfangen.
Der Functional Shift¶
Die größte Denkweise-Änderung ist nicht die Syntax — es ist der Programmierstil. TypeScript (und modernes JavaScript) umarmt funktionale Programmiermuster mehr als Delphi:
- Immutability: Bevorzugung von Werten, die sich nicht ändern
- Pure Functions: Functions ohne Side Effects
- Array Methods:
.map(),.filter(),.reduce()statt Schleifen - Arrow Functions: Prägnant und klar
Das ist nicht falsch—es ist anders. Viele Delphi-Entwickler finden, dass sie nach der Anpassung diesen Stil für bestimmte Problemtypen tatsächlich bevorzugen. Für andere bedeutet TypeScripts Unterstützung für Klassen, dass sie bei objektorientierten Ansätzen bleiben können.
Das Schöne ist, Sie haben Auswahlmöglichkeiten.
Warum das für Ihre Migration wichtig ist¶
Zu verstehen, dass TypeScript keine fremde Sprache ist—es ist ein Dialekt dessen, was Sie bereits kennen—ist entscheidend für die Planung Ihrer Migrationsstrategie.
Wenn Sie bewerten, ob Sie eine Delphi-Anwendung ins Web verschieben sollen, ist eine der größten Fragen: "Kann mein Team das lernen?" Die Antwort, wie Sie gesehen haben, ist ja. Die Syntax ist anders, aber die Konzepte sind dieselben.
Die wirklichen Fragen sind:
- Wie viel Ihrer Business Logic kann direkt übersetzt werden?
- Was muss für Web Architektur überdacht werden?
- Wie lange wird der Übergang dauern?
- Was können Sie in Phasen versus alles auf einmal machen?
Das sind strategische Fragen, keine technischen. Und sie sind für jede Anwendung unterschiedlich.
Wenn Sie bereit für Hilfe sind¶
Wenn Sie das lesen und über Ihre spezifischen Delphi-Anwendungen nachdenken, fragen Sie sich wahrscheinlich:
- "Könnten wir unser Order Management System migrieren?"
- "Was ist mit unserer Lagerverwaltungs-Anwendung?"
- "Wir haben 15 Jahre Business Logic—kann die erhalten werden?"
- "Wie lange würde das wirklich dauern?"
Das sind genau die Fragen, bei denen ich Delphi-Shops helfe. Der erste Schritt ist nicht Code schreiben—es ist zu verstehen, was Sie haben, was Ihre Ziele sind, und wie ein realistischer Migrationspfad aussieht.
Ich biete Migration Consulting speziell für Delphi-Anwendungen:
- Bewertung Ihrer aktuellen Delphi Codebase
- Identifikation dessen, was einfach übersetzt werden kann und was Überarbeitung braucht
- Architecture Planning für Web Deployment
- Team Training in TypeScript und modernem Web Development
- Hands-on Migration Assistance
- Post-Migration Support
Das Ziel ist nicht, Ihr Team zu ersetzen—es ist, ihnen das Wissen und Framework zu geben, um die Entwicklung nach der initialen Migration fortzusetzen.
Was als Nächstes kommt¶
Sie verstehen jetzt TypeScripts Kernfeatures und wie sie sich auf Ihr Delphi-Wissen abbilden. In Teil 3 werden wir React Components erkunden—denken Sie an sie als die VCL für Web-Anwendungen. Sie werden sehen, wie das Erstellen von User Interfaces in React konzeptionell ähnlich zum Erstellen von Forms in Delphi ist.
Die Komponenten (Components) werden anders aussehen, aber das Denken ist dasselbe: kleine, wiederverwendbare Teile zu größeren Anwendungen zusammenfügen.
Denken Sie über die Migration Ihrer Delphi-Anwendung nach? Lassen Sie uns ein Gespräch über Ihre spezifische Situation führen. Auch wenn Sie nur Optionen erkunden, kann ich Ihnen helfen zu verstehen, was involviert ist und was für Ihr Geschäft Sinn macht. Kontaktieren Sie mich!