TypeScript: Verbreitete Alternative zu JavaScript
Statische Typisierung → bessere Autovervollständigung und Fehlererkennung
Datentypen können explizit vom Entwickler angegeben werden oder von der Entwicklungsumgebung abgeleitet werden (engl. type inference)
Vortile:
Beispiel:
let names: Array<string> = [];
names.push('Alice');
names.push('Bob');
console.log(names[0].toUpperCase());
online mit TypeScript experimentieren:
Browser verstehen nur JavaScript, nicht TypeScript
→ Code muss von TypeScript in JavaScript übersetzt werden, um im Browser zu laufen
Beispielhafte Projekteinrichtung mit node / npm und vite:
npm create vite@latest
→ wähle Vanilla, dann TypeScript
Datentypen und Deklarationen, die wir verwenden werden:
Variablentypen können beim Deklarieren von Variablen angegeben werden:
let age: number = 32;
let age: number;
age = 32;
age++;
in vielen Fällen kennt TypeScript den Typ automatisch (keine Annotation notwendig):
let age = 32;
const age: number = 32;
const name: string = 'Alice';
const loggedIn: boolean = true;
const names: Array<string> = [];
names.push('Alice');
alternative Schreibweise:
const names: string[] = [];
names.push('Alice');
Typendeklaration:
let todo: {
id: number;
title: string;
completed: boolean;
};
Zuweisung:
todo = { id: 1, title: 'foo', completed: false };
optionale Einträge werden mit ?
gekennzeichnet
let todo: {
id: number;
title: string;
completed: boolean;
date?: string;
};
Variablen, die mehrere Typen annehmen können:
let width: string | number | undefined;
width = '16px';
width = 16;
Mit Type Aliases oder Interfaces können wir eine Typendeklaration unter einem Namen speichern
Type Aliases können etwas einfacher und flexibler sein als Interfaces (Vergleich auf StackOverflow)
Type Alias für ein Objekt:
type Todo = {
id: number;
title: string;
completed: boolean;
};
Interface für ein Objekt:
interface Todo {
id: number;
title: string;
completed: boolean;
}
const todos: Array<Todo> = [
{ id: 1, title: 'foo', completed: false },
{ id: 2, title: 'bar', completed: true },
];
Type Aliases und Interfaces sollten groß geschrieben werden (z.B. Todo
, nicht todo
)
function shorten(text: string, maxLen: number): string {
// ...
}
const shorten = (text: string, maxLen: number): string => {
// ...
};
Funktionen ohne Rückgabewert: void
function logMessage(message: string): void {
console.log(message);
}
Speichern einer Funktionssignatur unter einem Type Alias:
// a validator is a function that receives a string
// and returns a boolean
type Validator = (s: string) => boolean;
Anwenden der Type Alias:
const validateEmail: Validator = (s) => s.includes('@');
const validateYear: Validator = (s) => {
return new RegExp('^d{4}$').test(s);
};
Generic: allgemeine Typendeklaration, zu der bei der Anwendung nähere Informationen spezifiziert werden können (via <...>
)
Beispiel: Array
ist ein Generic
const names: Array<string> = ['Alice', 'Bob', 'Charlie'];
Beispiel: Promise
ist ein Generic
type User = { username: string; picture: string };
async function fetchUser(): Promise<User> {
// ...
}
Bei Funktionsaufrufen, die auf "externe" Ressourcen zugreifen kann TypeScript alleine oft nicht den Typ bestimmen.
Beispiele:
fetch()
JSON.parse()
document.getElementById()
Type Assertions erlauben es uns, ein bestehendes Objekt als einen bestimmten Typ zu behandeln
dies schlägt fehl:
// TypeScript type: HTMElement or null
const nameInput = document.getElementById('name-input');
// error: nameInput is possibly null
console.log(nameInput.id);
// error: nameInput is possibly null
console.log(nameInput.value);
"Assertion", dass ein Wert nicht null oder undefined ist mittels "!":
// TypeScript type: HTMLElement
const nameInput = document.getElementById('name-input')!;
// works
console.log(nameInput.id);
// error: property `value` does not exist on type `HTMLElement`
console.log(nameInput.value)
"Assertion", dass ein Wert einen bestimmten Typ hat mittels "as":
const nameInput = document.getElementById(
'name-input'
) as HTMLInputElement;
// works
console.log(nameInput.id);
// works
console.log(nameInput.value);
(Bemerkung: HTMLInputElement
ist eine Unterklasse von HTMLElement
)
Beispiel: Fetchen von JSON-Daten
type User = { username: string; picture: string };
async function fetchUser(): Promise<User> {
const res = await fetch('/api/user');
if (!res.ok) {
throw new Error('fetch failed');
}
const user = (await res.json()) as User;
return user;
}