TypeScript vs JavaScript - jak porovnat kód při migraci
Migroval jsem pár JS projektů na TypeScript. Na začátku jsem se v tom ztrácel - co jsem změnil? Kde jsem přidal typy? Diff nástroje mi zachránily život.
Proč jsem to dělal
1. Statická typová kontrola
// JavaScript - chyba se projeví až za běhu
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Zavoláno s nesprávným argumentem
calculateTotal("not an array"); // TypeError za běhu
// TypeScript - chyba odhalena při kompilaci
interface CartItem {
name: string;
price: number;
quantity: number;
}
function calculateTotal(items: CartItem[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// Kompilační chyba: Argument of type 'string' is not assignable
calculateTotal("not an array"); // Chyba už při psaní kódu
2. Lepší IntelliSense a autocomplete
S TypeScriptem získáte:
- Automatické doplňování - IDE ví, jaké vlastnosti má objekt
- Rychlá navigace - Ctrl+Click přejde na definici typu
- Refaktoring - Přejmenování proměnné se propaguje správně
- Dokumentace v kontextu - JSDoc komentáře se zobrazují inline
3. Snadnější údržba velkých projektů
U projektů nad 10 000 řádků kódu se TypeScript vyplatí:
- Méně runtime bugů (až o 15% méně dle studií)
- Rychlejší onboarding nových členů týmu
- Bezpečnější refaktoring
- Lepší spolupráce mezi frontend a backend týmy
Strategie migrace: Postupná vs. celková
Postupná migrace (doporučeno)
Většina týmů volí postupnou migraci, kdy konvertují soubor po souboru:
# Typická struktura projektu během migrace
src/
├── utils/
│ ├── helpers.ts # Migrováno
│ ├── validators.ts # Migrováno
│ └── formatters.js # Čeká na migraci
├── components/
│ ├── Button.tsx # Migrováno
│ └── Modal.js # Čeká na migraci
└── services/
├── api.ts # Migrováno
└── auth.js # Čeká na migraci
Výhody postupné migrace:
- Menší riziko zanesení chyb
- Snadnější code review
- Možnost učit se za pochodu
- Nezastaví vývoj nových funkcí
Celková migrace
Pro menší projekty (pod 5000 řádků) může být rychlejší konvertovat vše najednou:
# Nástroje pro hromadnou migraci
npx ts-migrate init . # Inicializace
npx ts-migrate rename src/ # Přejmenování .js na .ts
npx ts-migrate migrate src/ # Automatická migrace
Konfigurace TypeScript pro migraci
Základní tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": false,
"allowJs": true,
"checkJs": false,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Klíčové nastavení pro migraci:
allowJs: true- Povolí mix JS a TS souborůcheckJs: false- Nekontroluje JS souborystrict: false- Volnější pravidla na začátku
Postupné zpřísňování
Jak migrace postupuje, zpřísňujte pravidla:
// Fáze 1: Základní
{
"compilerOptions": {
"strict": false,
"noImplicitAny": false
}
}
// Fáze 2: Středně přísné
{
"compilerOptions": {
"strict": false,
"noImplicitAny": true,
"strictNullChecks": false
}
}
// Fáze 3: Přísné (cíl)
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}
Použití diff nástrojů při migraci
Porovnání před a po migraci
Při migraci jednotlivého souboru je klíčové porovnat, že se logika nezměnila:
Původní JavaScript (helpers.js):
export function formatDate(date) {
if (!date) return '';
const d = new Date(date);
const day = String(d.getDate()).padStart(2, '0');
const month = String(d.getMonth() + 1).padStart(2, '0');
const year = d.getFullYear();
return day + '.' + month + '.' + year;
}
export function formatCurrency(amount, currency) {
currency = currency || 'CZK';
return new Intl.NumberFormat('cs-CZ', {
style: 'currency',
currency: currency
}).format(amount);
}
export function slugify(text) {
return text
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
}
Migrovaný TypeScript (helpers.ts):
type DateInput = Date | string | number | null | undefined;
export function formatDate(date: DateInput): string {
if (!date) return '';
const d = new Date(date);
const day = String(d.getDate()).padStart(2, '0');
const month = String(d.getMonth() + 1).padStart(2, '0');
const year = d.getFullYear();
return day + '.' + month + '.' + year;
}
type CurrencyCode = 'CZK' | 'EUR' | 'USD' | 'GBP';
export function formatCurrency(
amount: number,
currency: CurrencyCode = 'CZK'
): string {
return new Intl.NumberFormat('cs-CZ', {
style: 'currency',
currency: currency
}).format(amount);
}
export function slugify(text: string): string {
return text
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
}
Co sledovat při porovnání
Při použití diff nástroje se zaměřte na:
1. Zachování logiky - Typové anotace by neměly měnit chování
Přidání typu k parametru a návratové hodnotě je v pořádku - přidáváme pouze typy, logika zůstává stejná.
2. Změny v default hodnotách
Pozor při omezení typu na enum - ověřte, že všechna volání používají podporované hodnoty.
3. Nové typy a interface
Nově definované typy zkontrolujte, zda odpovídají reálnému použití ve zbytku kódu.
Typické vzory migrace
1. Přidání typů k parametrům funkcí
// Před (JavaScript)
function processUser(user) {
console.log(user.name);
user.lastLogin = new Date();
}
// Po (TypeScript)
function processUser(user: User): void {
console.log(user.name);
user.lastLogin = new Date();
}
2. Definice rozhraní pro objekty
// Přidejte interface pro objekty
interface User {
id: number;
name: string;
email: string;
lastLogin?: Date;
}
const users: User[] = [];
3. Ošetření null a undefined
// Před (JavaScript)
function getUsername(user) {
return user.name;
}
// Po (TypeScript) - bezpečnější
function getUsername(user: User | null): string {
return user?.name ?? 'Anonymous';
}
4. Převod class komponent na typed
// Přidejte typy k třídám
interface UserCache {
[userId: string]: User;
}
class UserService {
private apiUrl: string;
private cache: UserCache;
constructor(apiUrl: string) {
this.apiUrl = apiUrl;
this.cache = {};
}
}
5. Migrace React komponent
import React, { FC } from 'react';
interface ButtonProps {
onClick: () => void;
children: React.ReactNode;
disabled?: boolean;
}
const Button: FC<ButtonProps> = ({ onClick, children, disabled }) => {
return (
<button onClick={onClick} disabled={disabled}>
{children}
</button>
);
};
export default Button;
Automatizace migrace pomocí nástrojů
ts-migrate od Airbnb
# Instalace
npm install -g ts-migrate
# Analýza projektu
ts-migrate check src/
# Automatická migrace s inferencí typů
ts-migrate migrate src/
# Výstup diff pro review
ts-migrate migrate src/ --dry-run > migration-diff.txt
Použití TypeScript Codemod
# Spuštění codemod pro přidání typů
npx jscodeshift -t ./codemods/add-types.js src/**/*.js
Vlastní migrační skript
// migrate-file.js
const fs = require('fs');
function migrateFile(inputPath) {
const content = fs.readFileSync(inputPath, 'utf8');
const outputPath = inputPath.replace('.js', '.ts');
// Základní transformace
let migrated = content
// Přidání typů k běžným parametrům
.replace(/function (\w+)\((\w+)\)/g, 'function $1($2: any)');
fs.writeFileSync(outputPath, migrated);
console.log('Migrated: ' + inputPath + ' -> ' + outputPath);
}
Code review při migraci
Checklist pro review migrovaného souboru
Při code review migrovaného kódu kontrolujte:
Typová bezpečnost:
- Jsou všechny
anytypy oprávněné? - Jsou interface kompletní?
- Jsou ošetřeny
nullaundefined? - Odpovídají typy reálnému použití?
Zachování funkcionality:
- Nezměnila se business logika?
- Jsou výchozí hodnoty stejné?
- Fungují edge cases jako předtím?
Kvalita kódu:
- Jsou typy dostatečně specifické (ne jen
anyneboobject)? - Jsou složené typy pojmenované (interface/type alias)?
- Je kód čitelnější než před migrací?
Git workflow pro migraci
# Vytvoření feature branch
git checkout -b feature/typescript-migration-utils
# Migrace jednoho modulu
mv src/utils/helpers.js src/utils/helpers.ts
# ... úpravy ...
# Commit s popisným názvem
git commit -m "refactor(utils): migrate helpers.js to TypeScript"
# Pull request s jasným diff
gh pr create --title "TypeScript migration: utils module"
Řešení běžných problémů
1. "Cannot find module" po přejmenování
# Problém: Import stále hledá .js soubor
import { helper } from './helpers.js'; // Nefunguje
# Řešení: Odstraňte příponu
import { helper } from './helpers'; // Funguje
2. Conflict typů z node_modules
// Problém: Nekompatibilní typy
import { Request } from 'express';
// Řešení: Vlastní rozšíření typů
// types/express.d.ts
declare namespace Express {
interface Request {
user?: {
id: string;
role: string;
};
}
}
3. Cirkulární závislosti typů
// Problém: User potřebuje Order, Order potřebuje User
// Řešení: Společný types soubor
// types/index.ts
export interface User {
id: string;
orders: Order[];
}
export interface Order {
id: string;
userId: string;
user?: User; // Lazy reference
}
4. Third-party knihovny bez typů
# Zkusit nainstalovat @types balíček
npm install --save-dev @types/lodash
# Pokud neexistuje, vytvořit vlastní deklaraci
# types/legacy-lib.d.ts
declare module 'legacy-lib' {
export function doSomething(input: string): void;
export const VERSION: string;
}
Měření postupu migrace
Sledování progress pomocí skriptu
#!/bin/bash
# migration-progress.sh
echo "=== TypeScript Migration Progress ==="
echo ""
total_js=$(find src -name "*.js" -o -name "*.jsx" | wc -l)
total_ts=$(find src -name "*.ts" -o -name "*.tsx" | wc -l)
total=$((total_js + total_ts))
if [ $total -gt 0 ]; then
percent=$((total_ts * 100 / total))
echo "JavaScript files: $total_js"
echo "TypeScript files: $total_ts"
echo "Progress: $percent%"
fi
Shrnutí
Migrace z JavaScriptu na TypeScript je investice, která se vyplatí. S pomocí diff nástrojů můžete:
- Bezpečně sledovat změny - Porovnáním před/po zajistíte, že se nezměnila logika
- Efektivně reviewovat - Diff jasně ukazuje, co se přidalo (typy) vs. co se změnilo (logika)
- Postupně migrovat - Soubor po souboru s plnou kontrolou
- Dokumentovat progress - Git historie ukazuje postup migrace
Klíčové tipy pro úspěšnou migraci:
- Začněte s
strict: falsea postupně zpřísňujte - Migrujte utility a sdílený kód jako první
- Používejte automatizační nástroje (ts-migrate)
- Code review každý migrovaný soubor
- Testujte průběžně
Potřebujete porovnat JavaScript a TypeScript verzi vašeho kódu? Vyzkoušejte náš Code Diff nástroj – podporuje oba jazyky a zvýrazní přesně, které řádky se změnily.
Vyzkoušejte PorovnejText.cz zdarma
Nejrychlejší český nástroj pro porovnání textů. Vše probíhá ve vašem prohlížeči, žádná registrace není potřeba.
Porovnat texty nyní →Související články
Git diff mi nestačí - kdy a proč používám online diff
Jako programátor pracuju s git diff denně. Ale někdy potřebuju rychle porovnat dva kousky kódu a nechce se mi kvůli tomu otvírat IDE. Kdy použít co.
Markdown a README - jak kontroluju změny
Píšete dokumentaci v Markdownu? Tady je jak porovnávám změny v README a dalších md souborech.
Jak automatizuju changelog z Git commitů
Ruční psaní release notes mě nebavilo. Tady je jak jsem to automatizoval pomocí Conventional Commits.