Pradžia / Programavimas / TypeScript – kodėl verta

TypeScript – kodėl verta

Kas iš tikrųjų yra TypeScript ir kodėl jis atsirado

JavaScript yra viena labiausiai paplitusių programavimo kalbų pasaulyje, bet ji turi vieną didelę problemą – ji buvo sukurta skubotai, devintajame dešimtmetyje, kai niekas negalvojo, kad žmonės rašys šimtatūkstantines kodų bazes tik šia kalba. Rezultatas? Kalba, kuri leidžia daryti beveik viską, bet dažnai tyliai, be jokių klaidų pranešimų, net kai tu darai kažką visiškai absurdiško.

TypeScript atsirado kaip Microsoft atsakymas į šią problemą. Pirmą kartą viešai pristatytas 2012 metais, jis iš esmės yra JavaScript su papildomu sluoksniu – statiniais tipais. Tai reiškia, kad prieš paleidžiant kodą, kompiliatorius patikrina, ar tu naudoji kintamuosius, funkcijas ir objektus taip, kaip jie turėtų būti naudojami.

Bet čia svarbu suprasti vieną dalyką: TypeScript nėra atskira kalba, kuri pakeičia JavaScript. Tai yra JavaScript superset – visas galiojantis JavaScript kodas yra taip pat galiojantis TypeScript kodas. Tai reiškia, kad galima pereiti prie TypeScript palaipsniui, be jokio „viskas arba nieko” sprendimo.

Tipų sistema – ne biurokratija, o saugos tinklas

Daugelis kūrėjų, pirmą kartą susidūrę su TypeScript, reaguoja skeptiškai: „Kodėl turėčiau rašyti daugiau kodo tam, kad gautų tą patį rezultatą?” Tai suprantama reakcija, bet ji pralepia esminį tašką.

Tipų sistema nėra apie tai, kad rašytum daugiau. Ji apie tai, kad kompiuteris tau padeda rasti klaidas anksčiau. Pažiūrėk į šį paprastą pavyzdį:


// JavaScript
function calculateTotal(price, quantity) {
  return price * quantity;
}

calculateTotal("10", 5); // Grąžina "1010101010" – string kartojimas, ne skaičiavimas!

// TypeScript
function calculateTotal(price: number, quantity: number): number {
  return price * quantity;
}

calculateTotal("10", 5); // Klaida kompiliavimo metu – tu matai problemą iš karto

Tai gali atrodyti kaip trivialus pavyzdys, bet realiame projekte, kur funkcijos kviečiamos iš dešimčių skirtingų vietų, tokios klaidos gali slypėti mėnesiais. TypeScript jas ištraukia į paviršių dar prieš tai, kai kodas pasiekia produkciją.

Tipų sistema taip pat padeda dokumentuoti kodą. Kai matai funkcijos signatūrą su tipais, iš karto supranti, ką ji tikisi gauti ir ką grąžina – nereikia skaityti visos implementacijos ar ieškoti komentarų.

Praktinis perėjimas – kaip pradėti be skausmo

Vienas didžiausių TypeScript privalumų yra tai, kad nereikia vienu ypu konvertuoti viso projekto. Microsoft tai suprojektavo taip, kad migracija galėtų vykti laipsniškai.

Pirmas žingsnis – paprasčiausiai pervadinti failus iš .js į .ts. Daugelis projektų tai padaro be jokių papildomų pakeitimų, nes, kaip minėjau, JavaScript yra galiojantis TypeScript. Tada galima konfigūracijoje (tsconfig.json) nustatyti "strict": false ir palaipsniui griežtinti taisykles.

Štai minimali tsconfig.json konfigūracija pradžiai:


{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": false,
    "esModuleInterop": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

Kai projektas kompiliuojasi be klaidų su šiais nustatymais, galima pradėti įjunginėti griežtesnes taisykles po vieną: "noImplicitAny", "strictNullChecks", "strictFunctionTypes". Kiekviena iš jų atvers naują klaidų sluoksnį, kurį reikės sutvarkyti, bet tai geriau nei atrasti tas klaidas produkcijoje.

Interfaces ir tipai – kaip modeliuoti duomenis

Viena iš sričių, kur TypeScript labiausiai pasirodo, yra duomenų modeliavimas. Kai dirbi su API atsakymais, duomenų bazės įrašais ar bet kokia struktūruota informacija, galimybė aprašyti, kaip tie duomenys atrodo, yra neįkainojama.

TypeScript siūlo du pagrindinius įrankius: interface ir type. Daugelis kūrėjų klausia, kurį naudoti. Trumpas atsakymas: naudok interface objektams ir klasėms, type – kai reikia sujungti kelis tipus arba aprašyti primityvus.


interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'editor' | 'viewer';
  createdAt: Date;
  address?: {        // Neprivalomas laukas
    city: string;
    country: string;
  };
}

// Union tipai – kai reikšmė gali būti viena iš kelių
type ApiResponse<T> = 
  | { status: 'success'; data: T }
  | { status: 'error'; message: string };

// Naudojimas
async function fetchUser(id: number): Promise<ApiResponse<User>> {
  // implementacija
}

Generics (<T>) yra vienas iš galingiausių TypeScript įrankių. Jie leidžia rašyti lankstų kodą, kuris vis tiek išlaiko tipo saugumą. Pavyzdyje aukščiau ApiResponse<T> gali būti naudojamas su bet kokiu tipu – ApiResponse<User>, ApiResponse<Product[]> ir pan.

TypeScript ir modernus frontend – React, Next.js ir draugai

Jei dirbi su React, TypeScript yra beveik būtinybė dideliuose projektuose. Komponentų props tipizavimas reiškia, kad IDE iš karto parodo, kokius props komponentas tikisi, ir įspėja, jei paduodi netinkamus duomenis.


interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: 'primary' | 'secondary' | 'danger';
  disabled?: boolean;
  size?: 'sm' | 'md' | 'lg';
}

const Button: React.FC<ButtonProps> = ({ 
  label, 
  onClick, 
  variant = 'primary',
  disabled = false,
  size = 'md'
}) => {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={`btn btn-${variant} btn-${size}`}
    >
      {label}
    </button>
  );
};

Next.js 13+ su App Router turi puikią TypeScript integraciją iš karto. Kai sukuri naują Next.js projektą su --typescript flag’u, viskas jau sukonfigūruota. Serverinių komponentų tipai, route handler’iai, middleware – visa tai turi aiškias tipo apibrėžtis.

Vienas praktinis patarimas: naudok satisfies operatorių (įvestą TypeScript 4.9), kai nori patikrinti, ar objektas atitinka tipą, bet vis tiek nori išlaikyti tikslesnį išvestinį tipą:


const config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3
} satisfies Record<string, string | number>;

// config.timeout vis dar yra 'number', ne 'string | number'

Dažniausios klaidos ir kaip jų išvengti

Yra keletas anti-pattern’ų, kuriuos dažnai matau TypeScript kode, ypač pas tuos, kurie tik pradeda:

Piktnaudžiavimas any tipu. Kai TypeScript rodo klaidą, kurią sunku greitai išspręsti, gundymas parašyti : any yra didelis. Bet tai iš esmės išjungia tipo patikrinimą tai vietai. Vietoj to naudok unknown – jis reikalauja, kad patikrintum tipą prieš naudodamas reikšmę:


// Blogai
function processData(data: any) {
  return data.value.toString(); // Nėra jokio patikrinimo
}

// Geriau
function processData(data: unknown) {
  if (typeof data === 'object' && data !== null && 'value' in data) {
    return String((data as { value: unknown }).value);
  }
  throw new Error('Invalid data format');
}

Per daug type assertion’ų (as). Rašyti something as User reiškia, kad tu sakei TypeScript’ui „pasitikėk manimi, aš žinau geriau.” Kartais tai būtina, bet jei tai darai dažnai, greičiausiai yra geresnė architektūra.

Ignoruojami strictNullChecks. Null ir undefined klaidos yra vienos dažniausių JavaScript klaidų. TypeScript su strictNullChecks: true priverčia tave eksplicitiškai tvarkyti šiuos atvejus. Naudok optional chaining (?.) ir nullish coalescing (??):


const userName = user?.profile?.displayName ?? 'Anonimai';

Įrankiai ir ekosistema – kas padaro darbą malonesnį

TypeScript be gerų įrankių yra kaip automobilis be GPS – veikia, bet galėtų būti daug patogiau. Štai ką rekomenduoju turėti savo darbo aplinkoje:

VS Code yra de facto standartas TypeScript kūrėjams, ir ne be reikalo – Microsoft kuria ir VS Code, ir TypeScript, tad integracija yra puiki. Autocomplete, inline klaidos, automatinis importas – visa tai veikia iš karto.

ESLint su typescript-eslint plugin’u prideda papildomą kodo kokybės sluoksnį. Rekomenduojama konfigūracija:


// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking'
  ],
  parserOptions: {
    project: './tsconfig.json'
  }
};

Zod biblioteka yra puikus papildymas TypeScript projektams, kur reikia validuoti išorinius duomenis (API atsakymai, formos). Ji leidžia apibrėžti schemą ir automatiškai išvesti TypeScript tipą iš jos:


import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string().min(2),
  email: z.string().email(),
  role: z.enum(['admin', 'editor', 'viewer'])
});

type User = z.infer<typeof UserSchema>; // Tipas išvedamas automatiškai

// Validacija runtime metu
const result = UserSchema.safeParse(apiResponse);
if (result.success) {
  // result.data yra User tipas
}

Kai TypeScript tampa gyvenimo būdu, o ne tik įrankiu

Po kurio laiko dirbant su TypeScript, pastebėji, kad pradedi mąstyti apie kodą kitaip. Prieš rašydamas funkciją, pirmiausia galvoji apie jos tipų signatūrą – ką ji gauna, ką grąžina, kokie kraštiniai atvejai galimi. Tai iš esmės yra dizaino proceso dalis, ne tik techninis formalumas.

Komandiniame darbe TypeScript tampa bendros kalbos pagrindu. Kai naujas kūrėjas ateina į projektą, jis gali žiūrėti į tipo apibrėžtis ir suprasti sistemos struktūrą daug greičiau nei skaitydamas komentarus ar dokumentaciją. Tipai yra gyva dokumentacija – jie negali pasenėti, nes jei jie neatitinka kodo, kompiliatorius praneša apie klaidą.

Refactoring’as su TypeScript yra visiškai kitoks patyrimas. Kai keiti funkcijos signatūrą ar pervadini lauką objekte, kompiliatorius iš karto parodo visas vietas, kurias reikia atnaujinti. Tai reiškia, kad galima drąsiai keisti kodą, žinant, kad jei kompiliacija praeina – didžioji dalis problemų jau išspręsta.

Žinoma, TypeScript nėra sidabrinė kulka. Jis neapsaugo nuo loginių klaidų, netinkamos architektūros ar prastos algoritmikos. Ir kartais, ypač su sudėtingomis generics konstrukcijomis, tipų klaidos gali tapti tikru galvos skausmu. Bet bendras balansas yra aiškiai teigiamas – projektai su TypeScript yra lengviau palaikomi, turi mažiau runtime klaidų ir yra draugiškesni naujiems komandos nariams.

Jei dar nenaudoji TypeScript, geriausias laikas pradėti buvo vakar. Antras geriausias laikas – šiandien. Pradėk nuo mažo projekto arba naujo feature’o esamame projekte. Pirmąsias kelias dienas gali atrodyti, kad rašai daugiau kodo. Po savaitės pradėsi pastebėti, kaip IDE padeda greičiau rašyti. Po mėnesio pradėsi stebėtis, kaip apskritai dirbai be jo.