Programování

TypeScript pro JavaScript vývojáře: rychlý a praktický start

Typy, rozhraní, union typy, generika a tsconfig vysvětlené pro ty, kdo umí JavaScript. Jak postupně převést existující projekt na TypeScript.

12. 10. 2026 · 4 min čtení

Pokud umíte JavaScript, umíte z velké části i TypeScript. TypeScript je JavaScript s typovými anotacemi, které se při kompilaci odstraní – za běhu nic nepřidávají. Jejich hodnota je v tom, že chyby jako undefined is not a function najde editor dřív, než kód spustíte.

Instalace a první kompilace

npm install --save-dev typescript
npx tsc --init

Vznikne tsconfig.json. Soubor index.ts zkompilujete příkazem npx tsc, kontrolu typů bez generování souborů spustíte přes npx tsc --noEmit. V projektech s Vite, Next.js apod. kompilaci řeší bundler a tsc --noEmit používáte jen na kontrolu (např. v CI).

Odvozování typů: nepište víc, než musíte

let count = 0;          // number – odvozeno
const name = 'Petr';    // typ 'Petr' (literál)
const items = [1, 2];   // number[]

count = 'deset';        // ❌ Type 'string' is not assignable to type 'number'

Anotace pište hlavně u parametrů funkcí a veřejných API. Návratový typ si TypeScript obvykle odvodí sám.

function formatPrice(amount: number, currency = 'CZK'): string {
  return new Intl.NumberFormat('cs-CZ', { style: 'currency', currency }).format(amount);
}

Objekty: type a interface

interface User {
  id: number;
  email: string;
  name?: string;              // nepovinné
  readonly createdAt: Date;   // nelze přepsat
}

type Role = 'admin' | 'editor' | 'viewer';

type UserWithRole = User & { role: Role };

Rozdíl je v praxi malý. interface lze rozšiřovat přes extends a slučovat deklarace, type umí union typy a složitější kombinace. Rozumná konvence: interface pro tvary objektů, type pro všechno ostatní. Hlavně buďte konzistentní.

Union typy a zužování

Union typ říká „jedno z těchto”. TypeScript pak hlídá, že jste ošetřili všechny možnosti:

function printId(id: number | string) {
  if (typeof id === 'string') {
    console.log(id.toUpperCase());   // zde je id: string
  } else {
    console.log(id.toFixed(0));      // zde je id: number
  }
}

Nejsilnější vzor je diskriminovaný union – stav, který nemůže být nekonzistentní:

type RequestState<T> =
  | { status: 'loading' }
  | { status: 'error'; error: string }
  | { status: 'success'; data: T };

function render(state: RequestState<User[]>) {
  switch (state.status) {
    case 'loading': return 'Načítám…';
    case 'error':   return `Chyba: ${state.error}`;
    case 'success': return `${state.data.length} uživatelů`;
  }
}

Nemůže nastat situace „loading je true a zároveň máme chybu”, kterou známe z tří samostatných proměnných.

null a undefined

Se zapnutým strict (doporučeno) vás TypeScript donutí ošetřit chybějící hodnoty:

const el = document.querySelector('#app');  // Element | null
el.innerHTML = 'Ahoj';                       // ❌ 'el' is possibly 'null'

if (el) el.innerHTML = 'Ahoj';               // ✅
el?.classList.add('ready');                  // ✅

Operátor ! (el!.innerHTML) kontrolu vypne. Používejte ho jen, když si jste opravdu jistí – je to slib kompilátoru, ne kontrola za běhu.

Generika

Generikum je typový parametr – funkce funguje pro libovolný typ a zachová ho:

function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

const n = first([1, 2, 3]);      // number | undefined
const s = first(['a', 'b']);     // string | undefined

Praktický příklad – typovaný fetch:

async function getJson<T>(url: string): Promise<T> {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json() as Promise<T>;
}

const users = await getJson<User[]>('/api/users');

Pozor: as je jen tvrzení. TypeScript nekontroluje, co server skutečně poslal. Pro data zvenčí (API, formuláře, localStorage) použijte validační knihovnu jako Zod, která typ ověří za běhu a zároveň ho odvodí.

Užitečné vestavěné typy

Typ Co dělá
Partial<User> všechny vlastnosti nepovinné
Required<User> všechny povinné
Pick<User, 'id' \| 'email'> jen vybrané vlastnosti
Omit<User, 'createdAt'> bez vybraných vlastností
Record<string, number> objekt se string klíči a number hodnotami
ReturnType<typeof fn> návratový typ funkce
function updateUser(id: number, changes: Partial<Omit<User, 'id'>>) { /* … */ }

any vs. unknown

any vypíná typovou kontrolu – a šíří se dál kódem. unknown říká „nevím, co to je” a před použitím vás donutí typ ověřit:

try {
  riskyOperation();
} catch (err: unknown) {
  const msg = err instanceof Error ? err.message : String(err);
  console.error(msg);
}

Rozumný tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "noEmit": true
  },
  "include": ["src"]
}

noUncheckedIndexedAccess přidá | undefined k přístupu přes index (arr[5], obj[key]), což odhalí reálné chyby. moduleResolution: "Bundler" je vhodné pro projekty s Vite nebo podobným bundlerem; pro čistý Node.js použijte "NodeNext" (spolu s "module": "NodeNext").

Postupná migrace existujícího JS projektu

  1. Do tsconfig.json přidejte "allowJs": true a "checkJs": false.
  2. Přejmenovávejte soubory .js → .ts jeden po druhém, začněte utilitami bez závislostí.
  3. Pro knihovny bez typů instalujte @types/nazev-balicku (např. npm i -D @types/lodash).
  4. Až to půjde, zapněte strict: true. Pokud je chyb moc, zapínejte dílčí volby (strictNullChecks atd.) postupně.

Alternativa bez přejmenování: JSDoc komentáře a // @ts-check na začátku .js souboru. Editor pak kontroluje typy i v čistém JavaScriptu.

Při migraci velkého projektu pomůže nahrát ho jako ZIP do projektu v ColdHost AI a nechat si navrhnout typy pro jednotlivé moduly – úpravy pak zkontrolujete přes tsc --noEmit a vrátíte do repozitáře jako pull request.