Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Declaration files (.d.ts) i DefinitelyTyped

Wyobraź sobie, że w Parku Jurajskim odkryto starożytne artefakty — tajemnicze tablice z opisami gatunków dinozaurów, napisane w nieznanym języku. Naukowcy musieli stworzyć "słowniki tłumaczeniowe", żeby zrozumieć, co te tablice opisują. W świecie TypeScript rolę takich słowników pełnią pliki deklaracji (.d.ts) — opisują kształt kodu JavaScript, który sam w sobie nie ma informacji o typach.

Czym są pliki .d.ts?

Pliki deklaracji (declaration files) to pliki z rozszerzeniem

.d.ts
, które zawierają wyłącznie informacje o typach — bez implementacji. Służą do opisania kształtu bibliotek JavaScript, modułów zewnętrznych i globalnych obiektów.

1// plik: dinosaur-tracker.d.ts
2// Opisujemy zewnętrzną bibliotekę JavaScript
3
4declare module 'dinosaur-tracker' {
5  export interface DinosaurPosition {
6    id: string;
7    species: string;
8    latitude: number;
9    longitude: number;
10    lastSeen: Date;
11  }
12
13  export function trackDinosaur(id: string): DinosaurPosition;
14  export function getAllPositions(): DinosaurPosition[];
15  export function setAlert(species: string, radius: number): void;
16}

Dzięki temu plikowi TypeScript wie, jakie funkcje i typy eksportuje moduł

dinosaur-tracker
, mimo że sam moduł jest napisany w czystym JavaScript.

Słowo kluczowe declare

Słowo

declare
informuje TypeScript: "ten element istnieje w runtime, ale nie musisz go kompilować — po prostu mi zaufaj". Używamy go do opisywania:

Zmiennych globalnych

1// Zmienna globalna dostępna w przeglądarce
2declare const PARK_CONFIG: {
3  name: string;
4  maxCapacity: number;
5  securityLevel: 'low' | 'medium' | 'high' | 'critical';
6};
7
8// Teraz możemy jej użyć z pełnym type safety
9console.log(PARK_CONFIG.name);
10console.log(PARK_CONFIG.securityLevel);
11// PARK_CONFIG.unknownField; // Błąd kompilacji!

Funkcji globalnych

1// Funkcja zdefiniowana w zewnętrznym skrypcie
2declare function initializeFence(
3  zone: string,
4  voltage: number
5): { active: boolean; zone: string };
6
7declare function emergencyShutdown(): void;
8
9// Użycie z type safety
10const fence = initializeFence('raptor-paddock', 10000);
11console.log(fence.active); // OK
12// console.log(fence.power); // Błąd! Nie ma takiej właściwości

Klas i namespace

1declare class SecuritySystem {
2  constructor(zones: string[]);
3  arm(zone: string): void;
4  disarm(zone: string): void;
5  getStatus(): Record<string, boolean>;
6}
7
8declare namespace ParkAPI {
9  interface Visitor {
10    id: string;
11    name: string;
12    ticket: 'standard' | 'vip' | 'researcher';
13  }
14
15  function registerVisitor(name: string, ticket: Visitor['ticket']): Visitor;
16  function getVisitorCount(): number;
17}
18
19// Użycie
20const system = new SecuritySystem(['zone-a', 'zone-b']);
21system.arm('zone-a');
22
23const visitor = ParkAPI.registerVisitor('Alan Grant', 'researcher');
24console.log(visitor.ticket);

Pisanie własnych plików deklaracji

Kiedy korzystasz z biblioteki JavaScript, która nie ma typów, musisz napisać własny plik

.d.ts
:

1// plik: types/legacy-dino-db.d.ts
2// Opisujemy starą bibliotekę JS do zarządzania bazą dinozaurów
3
4declare module 'legacy-dino-db' {
5  export interface DinosaurRecord {
6    id: string;
7    species: string;
8    diet: 'herbivore' | 'carnivore' | 'omnivore';
9    weight: number;
10    height: number;
11    dangerLevel: 1 | 2 | 3 | 4 | 5;
12  }
13
14  export interface QueryOptions {
15    limit?: number;
16    offset?: number;
17    sortBy?: keyof DinosaurRecord;
18    order?: 'asc' | 'desc';
19  }
20
21  export class DinoDB {
22    constructor(connectionString: string);
23    connect(): Promise<void>;
24    disconnect(): Promise<void>;
25    findAll(options?: QueryOptions): Promise<DinosaurRecord[]>;
26    findById(id: string): Promise<DinosaurRecord | null>;
27    insert(record: Omit<DinosaurRecord, 'id'>): Promise<DinosaurRecord>;
28    update(id: string, data: Partial<DinosaurRecord>): Promise<DinosaurRecord>;
29    delete(id: string): Promise<boolean>;
30  }
31
32  // Eksport domyślny
33  export default DinoDB;
34}

DefinitelyTyped i paczki @types

DefinitelyTyped to ogromne repozytorium na GitHubie, zawierające pliki deklaracji dla tysięcy bibliotek JavaScript. Zamiast pisać własne pliki

.d.ts
, możesz zainstalować gotowe typy:

1// Instalacja typów dla popularnych bibliotek:
2// npm install --save-dev @types/express
3// npm install --save-dev @types/lodash
4// npm install --save-dev @types/node
5
6// Po instalacji @types/express możesz pisać:
7import express, { Request, Response, NextFunction } from 'express';
8
9const app = express();
10
11// TypeScript zna typy Request, Response, NextFunction
12app.get('/dinosaurs/:id', (req: Request, res: Response) => {
13  const dinoId: string = req.params.id;
14  res.json({ id: dinoId, species: 'T-Rex' });
15});
16
17// Bez @types/express - brak informacji o typach!
18// req, res byłyby typu "any"

Jak TypeScript znajduje pliki deklaracji?

TypeScript szuka typów w następującej kolejności:

1// 1. Typy wbudowane w paczkę (pole "types" w package.json)
2// Wiele nowoczesnych bibliotek ma wbudowane .d.ts
3// np. axios, zod, prisma
4
5// 2. Paczki @types z node_modules/@types/
6// Automatycznie rozpoznawane przez TypeScript
7// np. @types/react, @types/node
8
9// 3. Własne pliki .d.ts w projekcie
10// Konfiguracja w tsconfig.json:
11// {
12//   "compilerOptions": {
13//     "typeRoots": ["./node_modules/@types", "./types"],
14//     "types": ["node", "express"]
15//   },
16//   "include": ["src/**/*", "types/**/*"]
17// }
18
19// 4. Triple-slash directives (rzadko używane)
20/// <reference types="node" />
21/// <reference path="./custom-types.d.ts" />

Ambient module declarations

Czasami potrzebujesz zadeklarować moduł, który nie jest pakietem npm — na przykład plik CSS, obraz lub plik JSON:

1// plik: types/assets.d.ts
2
3// Import plików CSS
4declare module '*.css' {
5  const classes: Record<string, string>;
6  export default classes;
7}
8
9// Import plików graficznych
10declare module '*.png' {
11  const src: string;
12  export default src;
13}
14
15declare module '*.svg' {
16  const content: string;
17  export default content;
18}
19
20// Import plików JSON
21declare module '*.json' {
22  const value: Record<string, unknown>;
23  export default value;
24}
25
26// Teraz możesz importować te pliki z type safety:
27// import styles from './styles.css';
28// import logo from './logo.png';
29// import dinoData from './dinosaurs.json';

Rozszerzanie istniejących typów (Declaration Merging w .d.ts)

Pliki

.d.ts
pozwalają na rozszerzanie typów z zewnętrznych bibliotek:

1// plik: types/express-extension.d.ts
2// Rozszerzamy typy Express o dodatkowe pola
3
4import 'express';
5
6declare module 'express' {
7  interface Request {
8    userId?: string;
9    parkZone?: string;
10    securityClearance?: 'visitor' | 'staff' | 'admin';
11  }
12}
13
14// Teraz w kodzie aplikacji:
15// app.use((req, res, next) => {
16//   req.userId = 'USR-001';       // OK - TypeScript zna to pole
17//   req.parkZone = 'zone-a';      // OK
18//   req.securityClearance = 'staff'; // OK
19//   next();
20// });

Pliki deklaracji (.d.ts) i ekosystem DefinitelyTyped to fundamenty pracy z TypeScript w realnym świecie. Dzięki nim możesz korzystać z tysięcy bibliotek JavaScript z pełnym type safety — jak naukowcy w Parku Jurajskim, którzy dzięki "słownikom tłumaczeniowym" potrafili odczytać nawet najstarsze tablice z informacjami o dinozaurach.

Przejdź do CodeWorlds