© SkillUp/Shutterstock.com
TypeScript von A bis Z - Teil 2

Weiterführende TypeScript-Konzepte


Die statische Typisierung von TypeScript erleichtert die Programmierung und verbessert die Qualität deutlich, ohne die Flexibilität einzuschränken. Auf dieser Basis haben sich die TypeScript-Entwickler einige hilfreiche weiterführende Konzepte überlegt.

Im ersten Teil ging es um die Vorteile eines typisierten JavaScripts, angefangen von statischen Transpiler-Überprüfungen bis hin zur Unterstützung von IDE-Tools, die eine statische Typüberprüfung voraussetzen. In diesem Teil beschäftigen wir uns mit weiterführenden Konzepten, die auf die statische Typisierung aufbauen und die Effizienz und Qualität der Programmierung erhöhen.

Überblick über die angepasste Dateistruktur

Durch die Erweiterung der Beispiele des letzten Artikels ergibt sich folgende angepasste Dateistruktur. Die neuen Dateien sind fett markiert und werden im Text und in den entsprechenden Listings beschrieben.

  • TS02 (Rootfolder des Beispiels)

    • package.json

    • index.html

    • src

      • app.ts

      • customer.i.ts

      • customer.ts

      • customer.view.ts

      • dataLoader.i.ts: Interface zum Datenladen

      • decorator.ts: Enthält einen Klassen-Decorator

      • mockDataLoader.ts: Mock-Datenladen

      • order.i.ts: Interface für Bestellungen

      • order.ts: Implementierung der Klasse für Bestellungen

      • tsconfig.json

Manche mögen’s generisch – Generics

Generics ermöglichen das Parametrieren von Datentypen. Als typisches Beispiel werden meistens Containerdatentypen wie z. B. Map herangezogen. Die Funktionalität einer Map ist immer gleich, egal welche Objekte welchen Typs darin gespeichert werden. Beispielsweise legt das Statement let customerMap = new Map<string, ICustomer>() eine Map an, von der der Transpiler weiß, dass darin nur ICustomers abgelegt werden dürfen. Das erhöht die Qualität, da der Transpiler irrtümliches Einfügen von Objekten eines falschen Typs verhindert.

Listing 1: dataLoader.i.ts

/**  * Class interface.  * TypeScript's way of declaring class parameter types,   * e.g. in generic functions.  */ export interface IClass<T> { new(...varargs: any[]): T; prototype: any; name: string; } /**  * Operators for comparing value and nesting criteria.  */ export enum CompareOperator { eq, new, lt, lte, gt, gte, like }; export enum CriteriaOperator { and, or }; /**  * Criteria for loading objects of a specific class.  */ export interface ILoadCriteria<T> { clazz: IClass<T>; criteria: IComplexCriteria<T> } /**  * A combination of search-criteria.  */ export interface IComplexCriteria<T> { operator: CriteriaOperator; criteria: ICriteria<T>[]; } /**  * "Recursive" type-union enables nesting of criteria:   * ICriteria can either be an ICondition or   * an IComplexCriteria. IComplexCriteria contains ICriteria in return.  */ export type ICriteria<T> = ICondition<T> | IComplexCriteria<T> /**  * A condition.  */ export interface ICondition<T> { poperty: string, operator: CompareOperator value: string | number, } /**  * Load data from backend.  */ export interface IDataLoader { load<T>(criteria: ILoadCriteria<T>): Promise<T[]>; }

Listing 2: Angepasste app.ts – verwendet IDataLoader

// The name "<moduleName>.js" is required by the browser.  // It's not valid to omit the file extension. // In a "real app", we would use a bundler like webpack and simply // write "import { Customer } from "./customer"" without extension. import { ICustomer } from "./customer.i.js"; import { Customer } from "./customer.js"; import { CustomerView } from "./customer.view.js"; import { IDataLoader, CompareOperator, CriteriaOperator, ILoadCriteria } from "./dataLoader.i.js"; import { MockDataLoader } from "./mockDataLoader.js"; export class App { protected appContainer: HTMLElement;  // Would be injected in real app. protected dataLoader: IDataLoader = new MockDataLoader(); constructor(protected memberAndItsInitialization?: number) { } start() { console.log(`App.start`); let splashScreen = document.getElementById("splashScreen")!; window.setTimeout(() => { splashScreen.style.display = "none"; }, 1000)  // "!" means: Regard return value as not null.  // Otherwise, compiler would raise error "object possibly null". this.appContainer = document.getElementById("appContainer")!; if (!this.appContainer) { throw new Error(`Cannot find div "appContainer" in html document.`); }  // "Navigate" to the customers' view. this.gotoCustomersView(); } async gotoCustomersView() { let customers = await this.simulateLoadCustomers(); let customerView = new CustomerView(this.appContainer); customerView.render(customers); } async simulateLoadCustomers(): Promise<ICustomer[]> { // Search for Customers whose   // firstName == John   // and whose lastName like "D%"   // and (who live in city "Linz" or in city "Frankfurt") const sampleCriteria: ILoadCriteria<ICustomer> = { clazz: Customer, criteria: { operator: CriteriaOperator.and, criteria: [ { poperty: "firstName", operator: CompareOperator.eq, value: "John" }, { poperty: "lastName", operator: CompareOperator.like, value: "D%" }, { operator: CriteriaOperator.or, criteria: [ { poperty: "city", operator: CompareOperator.eq, value: "Linz" }, { poperty: "city", operator: CompareOperator.eq, value: "Frankfurt" }, ] } ] } } return this.dataLoader.load(sampleCriteria); } } function start() { console.log(`app.ts.start`); let app = new App(); app.start(); } start();

Listing 3: MockDataLoader.ts

import { Customer } from "./customer.js"; import { IDataLoader, ILoadCriteria } from "./dataLoader.i"; import { Order } from "./order.js"; export class MockDataLoader implements IDataLoader {  /**  * Mock data-loading from backend.  * @param criteria   */ async load<T>(criteria: ILoadCriteria<T>): Promise<T[]> { let promise = new Promise(( resolve: (resultset: T[]) => void, reject: (reason: any) =>...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang