JavaScript er dynamisk typet, hvilket er genuint nyttigt — du kan iterere hurtigt, prototype
frit og sende kode uden et byggetrin. Men den fleksibilitet har en pris: en hel klasse af fejl
opstår kun under kørsel. En funktion forventer en streng, modtager undefined, og din app bryder sammen
i produktion klokken 2 om natten. TypeScript
tilføjer et statisk typesystem oven på JavaScript, der fanger netop disse fejl på kompileringstidspunktet, i din
editor, inden noget sendes. Denne artikel forklarer, hvad det faktisk betyder i praksis — med rigtig kode,
ærlige afvejninger og uden hype.
Hvad TypeScript faktisk er
TypeScript er en supersæt af JavaScript: enhver gyldig .js-fil er også gyldig
.ts. Du kan omdøbe en fil, tilføje nul typeannotationer og den kompilerer fint. Det TypeScript
tilføjer — typeannotationer, interfaces, generics, enums — fjernes fuldstændigt på kompileringstidspunktet. Det
resulterende output er rent JavaScript. TypeScript ændrer ikke, hvad der kører; det ændrer, hvad du kan vide
inden det kører.
Compileren er tsc, installeret via
npm. Du peger den mod
dine .ts-filer, og den genererer .js-filer, som din runtime (Node.js, en browser, Deno)
kan udføre. Ingen TypeScript-specifik syntaks når nogensinde produktion — typerne eksisterer kun for
udvikleren og toolchainet. TypeScript-repositoriet på GitHub
er selv et stort TypeScript-projekt, hvilket giver dig et billede af, hvordan det skalerer.
# Compile a single file
npx tsc src/index.ts
# Compile a whole project (uses tsconfig.json)
npx tsc
# Watch mode — recompile on every save
npx tsc --watchDen centrale fordel: fejl inden kørsel
Her er det problem, TypeScript løser mest synligt. Forestil dig, at du arbejder med et API-svar,
hvor et felt kan være en streng eller null. I rent JavaScript fejler dette lydløst, indtil
det rammer produktion:
// JavaScript — no error until this actually runs with a null value
function formatDisplayName(user) {
return user.displayName.toUpperCase(); // 💥 TypeError if displayName is null
}
// This looks fine in isolation. The bug only appears when a user
// has no display name set — which might be rare, but it will happen.
const name = formatDisplayName({ displayName: null });TypeScript fanger dette, inden koden kører:
interface User {
id: number;
displayName: string | null;
email: string;
}
function formatDisplayName(user: User): string {
return user.displayName.toUpperCase();
// ❌ TypeScript error: Object is possibly 'null'.
// Property 'toUpperCase' does not exist on type 'null'.
}
// Fix: handle the null case explicitly
function formatDisplayName(user: User): string {
if (user.displayName === null) {
return user.email; // fall back to email
}
return user.displayName.toUpperCase(); // TypeScript now knows this is safe
}Dette er aha-øjeblikket for de fleste udviklere. Fejlmeddelelsen fortæller dig præcis, hvad der er galt
og hvor — i din editor, inden du kører en eneste linje. Med strict-tilstand aktiveret er TypeScript
særligt aggressiv til at fange null- og undefined-problemer via sin
strictNullChecks-funktion,
som er aktiveret som standard i strict-tilstand.
tsc betyder, at din kode har en typeuoverensstemmelse — TypeScript kan bevise, at den vil
fejle (eller måske fejle) uden at udføre den. Ret typefeilen, og du har elimineret den fejlklasse fuldstændigt.TypeScripts typesystem i oversigt
Du behøver ikke annotere alt. TypeScript udleder typer fra tildelinger, returværdier og funktionskald — ofte skriver du bare normalt JavaScript og får typekontrol gratis. Men det hjælper at kende annotationssyntaksen, når udledning ikke er tilstrækkelig.
// Primitives
const productName: string = 'Wireless Keyboard';
const price: number = 79.99;
const inStock: boolean = true;
// TypeScript infers these without annotations — same effect
const productName = 'Wireless Keyboard'; // inferred: string
const price = 79.99; // inferred: number
// Arrays
const tags: string[] = ['electronics', 'peripherals'];
const ratings: number[] = [4.5, 4.8, 4.2];
// Objects with interfaces
interface Product {
id: number;
name: string;
price: number;
category: string;
inStock: boolean;
description: string | null; // union type — string or null
}
// Union types — a value that could be one of several types
type Status = 'active' | 'inactive' | 'pending'; // string literal union
type ID = string | number; // string or number
// Function with typed parameters and return type
function formatProductCard(product: Product): string {
const stockLabel = product.inStock ? 'In Stock' : 'Out of Stock';
const desc = product.description ?? 'No description available';
return `${product.name} — $${product.price.toFixed(2)} (${stockLabel})
${desc}`;
}
// Generic function — works with any type, preserves it
function firstOrDefault<T>(items: T[], fallback: T): T {
return items.length > 0 ? items[0] : fallback;
}
const first = firstOrDefault(['a', 'b', 'c'], 'z'); // inferred: string
const num = firstOrDefault([1, 2, 3], 0); // inferred: numberTypeScript-håndbogen går i dybden med typesystemet — den er virkelig velskrevet og værd at læse, når du har styr på det grundlæggende.
Opsætning af TypeScript i et projekt
At tilføje TypeScript til et projekt tager cirka fem minutter. Du installerer compileren som en dev-afhængighed,
genererer en konfigurationsfil og begynder at omdøbe .js-filer til .ts i det tempo,
der giver mening.
# Install TypeScript as a dev dependency
npm install -D typescript
# Generate tsconfig.json with sensible defaults
npx tsc --initDen genererede tsconfig.json har dusinvis af muligheder, de fleste kommenteret ud. De vigtigste
at kende til:
{
"compilerOptions": {
"target": "ES2020", // what JavaScript version to output
"module": "commonjs", // module system (commonjs for Node, ESNext for bundlers)
"outDir": "./dist", // where compiled .js files go
"rootDir": "./src", // where your .ts source files live
"strict": true, // enables all strict type checks — highly recommended
"esModuleInterop": true, // smoother interop with CommonJS modules
"skipLibCheck": true // skip type checks on .d.ts files in node_modules
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}Aktiver altid strict: true på et nyt projekt. Det samler adskillige
kontroller, herunder strictNullChecks, noImplicitAny og
strictFunctionTypes. I en eksisterende codebase skal du måske aktivere disse gradvist —
men for alt greenfield, start strict.
TypeScript vs JavaScript — de reelle afvejninger
TypeScript har mange reelle fordele, men også reelle omkostninger. Her er en ærlig gennemgang:
- Autofuldførelse, der faktisk virker. Din editor kender formen af hvert objekt, så den kan foreslå de rigtige egenskabsnavne og metodesignaturer i stedet for at gætte. Dette alene sparer tid hver eneste dag.
- Fejl fanget inden de når produktion. Null-dereferences, forkerte argumenttyper, manglende påkrævede egenskaber — TypeScript bringer disse frem på skrivetidspunktet, ikke kl. 3 om morgenen i en hændelses-kanal.
- Sikrere refaktorering. Omdøb et felt på en interface, og TypeScript markerer øjeblikkeligt hvert kaldssted, der skal opdateres. I en stor JavaScript-codebase er den slags ændringer skræmmende.
- Koden dokumenterer sig selv. En funktionssignatur som
sendEmail(to: string, subject: string, body: string, options?: EmailOptions): Promise<SendResult>fortæller dig alt, hvad du behøver at vide, uden at læse implementeringen. - Bedre for teams. Når flere udviklere deler en codebase, danner typer en kontrakt mellem moduler. Du kan refaktorere din kode uden at ødelægge din kollegas.
Og de ærlige omkostninger:
- Det tilføjer et byggetrin. For et lille script eller en hurtig prototype er det at køre
tscinden du kan teste reel friktion. Vanilla JavaScript kører øjeblikkeligt. - Indledende opsætning tager tid. Konfigurering af
tsconfig.json, tilføjelse af type definitioner for tredjepartsbiblioteker (@types/expressosv.) og korrekt konfigurering af din editor er et par timers arbejde. anyunderminerer det hele. TypeScript har en flugtvej — du kan skrive noget somany, hvilket deaktiverer typekontrol for den værdi. Overforbruget afanybetyder, at du får friktionen fra TypeScript uden sikkerheden. Det er en krykke, ikke en løsning.- Tredjepartsbibliotekstyper kan halte bagud. Ikke alle npm-pakker leverer TypeScript-definitioner.
Nogle afhænger af community-vedligeholdte
@types/*-pakker, der kan være ufuldstændige eller forældede.
Hvor TypeScript virkelig skinner
TypeScript giver det største udbytte i tre situationer: store codebases, delte biblioteker og alt, der vil blive refaktoreret over tid. Typesystemet fungerer som levende dokumentation, der altid er opdateret — i modsætning til kommentarer eller README-filer.
Et praktisk eksempel: din app kalder et REST API, der returnerer brugerdata. Uden TypeScript stoler du på, at API'et returnerer det, du forventer. Med TypeScript modellerer du svaret og får øjeblikkelig feedback, hvis noget ikke stemmer:
interface ApiUser {
id: number;
username: string;
email: string;
avatarUrl: string | null;
createdAt: string; // ISO 8601 date string
role: 'admin' | 'editor' | 'viewer';
}
interface ApiResponse<T> {
data: T;
total: number;
page: number;
perPage: number;
}
async function fetchUsers(page: number): Promise<ApiResponse<ApiUser[]>> {
const response = await fetch(`/api/users?page=${page}`);
if (!response.ok) {
throw new Error(`Failed to fetch users: ${response.status}`);
}
return response.json() as Promise<ApiResponse<ApiUser[]>>;
}
// Now every consumer of this function knows exactly what they'll get
const result = await fetchUsers(1);
result.data.forEach(user => {
// TypeScript knows user.role is 'admin' | 'editor' | 'viewer'
// It will error if you try to access a property that doesn't exist
console.log(`${user.username} (${user.role})`);
});Når du har brug for at fravælge typekontrol midlertidigt, brug unknown i stedet for
any. Forskellen: any omgår stiltiende alle kontroller, mens unknown
tvinger dig til at indsnævre typen inden du bruger værdien — du får flugtvejsindhakket uden at miste sikkerhed
fuldstændigt.
// ❌ any — TypeScript trusts you completely, no checks
function processData(data: any) {
data.nonExistentMethod(); // no error — TypeScript looks away
}
// ✅ unknown — you have to prove what it is before using it
function processData(data: unknown) {
if (typeof data === 'string') {
console.log(data.toUpperCase()); // safe — TypeScript knows it's a string here
} else if (Array.isArray(data)) {
console.log(data.length); // safe — TypeScript knows it's an array
}
}TypeScript Playground er den hurtigste måde at eksperimentere med disse mønstre på. Indsæt kode, se det kompilerede JavaScript-output og eventuelle typefejl i realtid — ingen installation krævet.
Opsummering
TypeScript er ikke magi, og det er ikke rigtigt til hvert projekt. Men for enhver JavaScript-codebase der vokser, vedligeholdes af mere end én person, eller vil blive refaktoreret — ændrer det virkelig udviklingsoplevelsen. Typesystemet fanger en hel kategori af kørselsfejl, inden de sendes, gør autofuldførelse faktisk nyttig og omdanner refaktorering fra en risikabel manuel proces til noget, toolchainet håndterer.
Start med den officielle TypeScript-dokumentation — den er velstruktureret og begyndervenlig. TypeScript Playground er fremragende til at eksperimentere uden nogen opsætning. MDN TypeScript-glossarindgangen er et solidt enkeltsidesoversigtsdokument, hvis du vil have et andet perspektiv. Og hvis du arbejder med JSON-data i TypeScript, genererer værktøjet JSON til TypeScript på dette websted TypeScript-interfaces direkte fra en JSON-payload — nyttigt, når du hurtigt har brug for at modellere et API-svar uden at skrive typerne i hånden.