Zmienne i typy podstawowe
Trzy typy — liczba, tekst i wartość logiczna — wystarczą, żeby opisać większość danych w programie. W tej lekcji uczysz się je deklarować, chronić przed zmianą i bezpiecznie zamieniać jedne na drugie.
Praca w konsoli — wspólne zasady kursu
Środowisko: TypeScript 6.0 i Node.js 24 LTS (co najmniej 24.15.0). Każdy pełny przykład uruchamiaj osobno w src/index.ts. Bloki opisane jako celowy błąd służą do odczytania komunikatu kompilatora; nie powinny się uruchomić. Bloki JavaScript pokazują zachowanie JS, a fragmenty są oznaczone osobno. Celem kursu jest składnia TypeScriptu przed nauką Angulara
Czego się dziś nauczysz
- Zadeklarujesz zmienną z jawnym typem i bez niego
- Wybierzesz świadomie między
letaconst - Opiszesz różnicę między liczbą
7a tekstem"7" - Zamienisz tekst na liczbę i sprawdzisz, czy operacja się udała
- Wyjaśnisz, dlaczego
varnie pojawi się w tym kursie
Jak zadeklarować zmienną
Zmienna to nazwane miejsce w pamięci, w którym program przechowuje wartość. W TypeScripcie deklaracja ma zawsze ten sam układ:
let punkty : number = 0 ;
│ │ │ │
│ │ │ └─ wartość początkowa
│ │ └───────────── typ (opcjonalny)
│ └───────────────────────── nazwa
└───────────────────────────────── słowo kluczowe: let albo const
let punkty: number = 0;
let nick: string = "Karol";
let zalogowany: boolean = false;
const PODATEK: number = 0.23;
const SZKOLA: string = "ZSMI Lębork";
Zasady nazywania zmiennych
- Nazwa może zawierać litery, cyfry oraz znaki
_i$, ale nie może zaczynać się od cyfry. - Wielkość liter ma znaczenie:
punktyiPunktyto dwie różne zmienne. - Przyjęty zwyczaj to camelCase:
liczbaUczniow,sredniaOcen. - Stałe konfiguracyjne pisze się często wersalikami:
MAX_PUNKTOW. - Nazwa ma mówić, co zawiera.
xidane2to nazwy, których nie zrozumiesz za tydzień.
Polskie znaki w nazwach
Technicznie średnia zadziała, ale w praktyce nazwy pisze się bez ogonków —
unika się kłopotów z kodowaniem plików i konsolą Windows. Komentarze i teksty wypisywane
użytkownikowi piszemy oczywiście normalnie, po polsku.
let czy const?
Różnica jest jedna: wartość zmiennej let można później podmienić, a const —
nie.
let zycia: number = 3;
zycia = zycia - 1; // OK — 2
console.log(zycia);
const nazwaGry: string = "Snake";
nazwaGry = "Tetris"; // Błąd TS2588: Cannot assign to 'nazwaGry'
// because it is a constant.
Zasada praktyczna, którą stosują zawodowi programiści: zaczynaj od const.
Dopiero gdy okaże się, że wartość musi się zmieniać, zmień deklarację na let.
Dzięki temu, czytając kod, od razu widzisz, które wartości są ustalone raz na zawsze,
a które trzeba śledzić.
A co z var?
W starszym kodzie JavaScript zobaczysz jeszcze var. Ma ono nietypowy zasięg —
zmienna „wycieka” poza blok, w którym powstała:
if (true) {
var stary = "widoczny na zewnątrz";
let nowy = "widoczny tylko tutaj";
}
console.log(stary); // działa — i o to właśnie chodzi z var
console.log(nowy); // Błąd TS2304: Cannot find name 'nowy'.
To źródło trudnych do wytropienia pomyłek, dlatego w tym kursie var nie użyjemy ani razu.
Jeśli zobaczysz je w cudzym kodzie, wiesz, że pochodzi sprzed 2015 roku.
Trzy typy, na których stoi wszystko
| Typ | Do czego służy | Przykładowe wartości |
|---|---|---|
number |
Wszystkie liczby — całkowite i z częścią dziesiętną, dodatnie i ujemne | 0, 42, -7, 3.14, 1e6, 0xFF |
string |
Tekst dowolnej długości, także pusty | "Ala", 'x', `wynik`, "" |
boolean |
Odpowiedź na pytanie tak/nie | true, false |
W TypeScripcie nie ma osobnego int i double
Do zwykłych obliczeń używamy number: obsługuje liczby całkowite i ułamkowe. 7 / 2 daje 3.5; Math.trunc(7 / 2) odrzuca ułamek. Osobny bigint dotyczy liczb całkowitych i ma inne reguły — na razie nie jest potrzebny w naszych zadaniach.
Apostrofy, cudzysłowy i backticki
Tekst możesz zapisać na trzy sposoby. Pierwsze dwa są równoważne, trzeci ma dodatkową moc:
const a: string = "cudzysłów";
const b: string = 'apostrof';
const c: string = `backtick`; // klawisz obok lewego Shifta
const imie: string = "Zosia";
const punkty: number = 87;
// sklejanie plusem — czytelne przy dwóch elementach
console.log("Uczeń " + imie + " zdobył " + punkty + " pkt");
// szablon w backtickach — czytelniejszy przy większej liczbie
console.log(`Uczeń ${imie} zdobył ${punkty} pkt`);
Wewnątrz backticków zapis ${…} wstawia wartość zmiennej albo wynik obliczenia.
Ten sposób poznamy dokładniej w lekcji 5.
Wnioskowanie typu — kiedy typu nie trzeba pisać
TypeScript potrafi sam odgadnąć typ z wartości początkowej. Obie linijki poniżej znaczą dokładnie to samo:
let wiek: number = 18; // typ zapisany jawnie
let wiek2 = 18; // typ wywnioskowany — też number
wiek2 = 19; // OK
wiek2 = "dziewiętnaście"; // Błąd TS2322 — typ został ustalony przy deklaracji
Zwróć uwagę: brak zapisanego typu nie oznacza, że zmienna przyjmie cokolwiek. Typ został ustalony w momencie deklaracji i już się nie zmieni.
Kiedy pisać typ, a kiedy odpuścić
Pomiń, gdy wartość mówi sama za siebie
const cena = 19.99; — każdy widzi, że to liczba. Dopisywanie : number
tylko zaśmieca kod.
Napisz, gdy zaczynasz od pustego
Przy deklaracji bez wartości, np. let wynik: string;, jawny typ określa planowany rodzaj danych. Przy let wynik = ""; kompilator już potrafi wywnioskować string z pustego tekstu.
Zawsze przy parametrach funkcji
Przy zwykłej deklaracji funkcji zapisujemy typy parametrów. W callbackach kompilator często wywnioskuje je z kontekstu — zobaczysz to w lekcji 13.
Napisz, gdy chcesz coś wymusić
Kiedy zależy ci, żeby zmienna była właśnie takiego typu i żeby kompilator pilnował tego od pierwszej linijki.
Zmienna bez wartości i bez typu to pułapka
Nie zostawiaj niejasnego let cos;. Jego typ początkowy to any, choć analiza przepływu może później śledzić przypisania. Jawny typ albo wartość początkowa jasno pokazują zamiar.
Karta postaci w grze
Program przechowuje statystyki bohatera, symuluje otrzymanie obrażeń i wypisuje raport. Wszystkie typy poznane w tej lekcji pracują tu razem.
// ── dane, które nigdy się nie zmieniają ──
const nazwaPostaci = "Cień z Lęborka";
const MAKS_ZYCIA = 100;
const OBRAZENIA_OD_PULAPKI = 35;
// ── dane, które zmieniają się w trakcie gry ──
let zycie = MAKS_ZYCIA;
let poziom = 1;
let zloto = 12.5;
let czyZatruty = false;
// ── bohater wpada w pułapkę ──
zycie = zycie - OBRAZENIA_OD_PULAPKI;
czyZatruty = true;
zloto = zloto - 2.5;
// ── obliczenia pomocnicze ──
const procentZycia = (zycie / MAKS_ZYCIA) * 100;
const zyje = zycie > 0;
// ── raport ──
console.log("=== " + nazwaPostaci + " ===");
console.log("Poziom: " + poziom);
console.log("Życie: " + zycie + "/" + MAKS_ZYCIA + " (" + procentZycia + "%)");
console.log("Złoto: " + zloto.toFixed(2) + " szt.");
console.log("Zatruty: " + (czyZatruty ? "tak" : "nie"));
console.log("Stan: " + (zyje ? "walczy dalej" : "koniec gry"));
=== Cień z Lęborka ===
Poziom: 1
Życie: 65/100 (65%)
Złoto: 10.00 szt.
Zatruty: tak
Stan: walczy dalej
Omówienie
| Linia | Dlaczego tak |
|---|---|
const MAKS_ZYCIA = 100; |
Wartość ustalona raz na zawsze, więc const. Wersaliki sygnalizują, że to stała
konfiguracyjna. Typ number został wywnioskowany. |
let zycie = MAKS_ZYCIA; |
Zaczyna od pełnej puli, ale będzie się zmieniać — dlatego let.
Typ też wywnioskowany, bo MAKS_ZYCIA jest liczbą. |
zycie = zycie - OBRAZENIA_OD_PULAPKI; |
Prawa strona liczy się pierwsza (100 − 35), dopiero wynik trafia do zmiennej.
Zapis skrócony to zycie -= 35; |
const zyje = zycie > 0; |
Porównanie daje wartość logiczną, więc zyje ma typ boolean
bez żadnego if. |
zloto.toFixed(2) |
Zamienia liczbę na tekst z dwoma miejscami po przecinku. Zwraca string —
dlatego wynik to "10.00", a nie 10. |
czyZatruty ? "tak" : "nie" |
Operator warunkowy: jeśli warunek jest prawdziwy, weź wartość przed dwukropkiem, w przeciwnym razie tę po nim. |
Sprawdź to sam
Zmień OBRAZENIA_OD_PULAPKI na 120 i uruchom program ponownie.
Zobaczysz ujemne życie i „koniec gry”. Potem spróbuj przypisać coś do MAKS_ZYCIA —
kompilator natychmiast zaprotestuje.
Zamiana typów
Wartość może trafić do programu jako tekst, np. „42”. Zanim policzysz na takim tekście, przekształć go świadomie. Dane z innych źródeł nie zawsze są tekstem — po odczytaniu JSON mogą być także liczbą, obiektem lub tablicą.
const wpisane = "42";
// tekst → liczba
const jakoLiczba = Number(wpisane); // 42
const calkowita = parseInt("3.99", 10); // 3 (obcina część dziesiętną)
const rzeczywista = parseFloat("3.99"); // 3.99
// liczba → tekst
const liczba = 255;
const tekst = String(liczba); // "255"
const zDwoma = liczba.toFixed(2); // "255.00"
const dwojkowo = liczba.toString(2); // "11111111"
// cokolwiek → boolean
const czyPuste = Boolean(""); // false
const czyCos = Boolean("abc"); // true
console.log(jakoLiczba + 8); // 50 — dodawanie liczb
console.log(wpisane + 8); // 428 — sklejanie tekstu!
Gdy zamiana się nie uda
Number("abc") nie zgłasza błędu — zwraca specjalną wartość NaN
(not a number). Co gorsza, NaN ma typ number, więc kompilator
niczego nie zauważy. Trzeba to sprawdzić samodzielnie:
const podane = "trzydzieści";
const wynik = Number(podane);
console.log(wynik); // NaN
console.log(wynik + 10); // NaN — zaraża wszystkie obliczenia
console.log(Number.isNaN(wynik)); // true — tak to wykrywasz
if (Number.isNaN(wynik)) {
console.log("To nie jest poprawna liczba!");
}
Nigdy nie porównuj z NaN przez ===
wynik === NaN zawsze daje false — nawet gdy wynik naprawdę
jest NaN. To jedyna wartość w języku, która nie równa się samej sobie.
Używaj wyłącznie Number.isNaN(…).
Pięć pułapek początkującego
| Kod | Co jest nie tak |
|---|---|
const licznik = 0; licznik = 1; |
Wartości const nie da się podmienić. Tu powinno być let. |
let suma; suma = suma + 5; |
Zmienna bez wartości początkowej ma undefined; wynik to NaN.
Zawsze zaczynaj od let suma = 0; |
const wiek: number = "18"; |
Cudzysłowy robią z tego tekst. Błąd TS2322. |
console.log("5" + 3); |
Sklejanie zamiast dodawania — wynik "53". Najpierw Number("5"). |
const suma = 0.1 + 0.2; |
0.1 + 0.2 daje 0.30000000000000004, ponieważ number używa binarnego zapisu zmiennoprzecinkowego. toFixed(2) formatuje wynik jako tekst; nie jest ogólnym sposobem porównywania liczb. Do przybliżonych pomiarów dobierz tolerancję, a proste kwoty licz w całych groszach. |
Skąd tu if
W przykładach pojawia się instrukcja warunkowa if. Na razie wystarczy odczytać ją tak, jak brzmi: „jeżeli warunek jest prawdziwy, wykonaj to, co w klamrach”. Pełne omówienie — razem z else, zagnieżdżeniami i typowymi pułapkami — czeka w lekcji poświęconej if,else.
Zadania
Bez uruchamiania programu zapisz, co wypisze każda linia. Potem sprawdź w edytorze.
console.log(10 + 5);
console.log("10" + 5);
console.log("10" - 5);
console.log(Number("10") + 5);
console.log(7 / 2);
console.log(true + 1);
pokaż rozwiązanie
15 dodawanie dwóch liczb
105 "+" przy tekście skleja
5 "-" nie ma wersji tekstowej, więc JS zamienia "10" na liczbę
15 jawna zamiana — tak trzeba to robić
3.5 jeden typ number, brak dzielenia całkowitego
2 true zamienia się w 1 (nie pisz tak w prawdziwym kodzie!)
Zadeklaruj zmienne opisujące zakup: nazwę produktu, cenę jednostkową, liczbę sztuk oraz to,
czy klient ma legitymację (rabat 10%). Policz i wypisz wartość przed rabatem, kwotę rabatu
i kwotę do zapłaty — wszystkie z dokładnością do dwóch miejsc po przecinku.
Zastanów się przy każdej zmiennej, czy powinna być let, czy const.
pokaż przykładowe rozwiązanie
const produkt = "kanapka";
const cena = 6.5;
const sztuk = 3;
const maLegitymacje = true;
const STAWKA_RABATU = 0.10;
const wartosc = cena * sztuk;
const rabat = maLegitymacje ? wartosc * STAWKA_RABATU : 0;
const doZaplaty = wartosc - rabat;
console.log("Produkt: " + produkt + " x" + sztuk);
console.log("Wartość: " + wartosc.toFixed(2) + " zł");
console.log("Rabat: " + rabat.toFixed(2) + " zł");
console.log("Do zapłaty: " + doZaplaty.toFixed(2) + " zł");
Wszystko może być const, bo żadna wartość nie jest tu podmieniana —
każda powstaje raz, na podstawie poprzednich.
Ten program ma cztery błędy. Znajdź je, popraw i zapisz, jaki kod błędu zgłosił kompilator przy każdym z nich.
const 1uczen: string = "Ala";
let srednia: number = "4.5";
const MAKS = 100;
MAKS = 120;
let wynik;
wynik = srednia * 2;
console.log(wynik.toUpperCase());
pokaż rozwiązanie
const uczen: string = "Ala"; // nazwa nie może zaczynać się od cyfry
let srednia: number = 4.5; // TS2322 — liczba bez cudzysłowów
const MAKS = 100; // TS2588 — usuwamy próbę podmiany
let wynik: number = 0; // typ + wartość początkowa
wynik = srednia * 2;
console.log(wynik.toFixed(1)); // TS2339 — liczba nie ma toUpperCase()
Masę i wzrost zapisz jako tekst, np. „72” i „1.78”. To ćwiczenie obliczania wzoru, nie ocena zdrowia. Odrzuć pusty tekst, nieskończoności i wartości niedodatnie. Dopiero potem policz masa / (wzrost * wzrost) i wypisz jedno miejsce po przecinku.
pokaż wskazówki
Po zamianie sprawdź Number.isFinite(masa) && Number.isFinite(wzrost) && masa > 0 && wzrost > 0. Przed konwersją sprawdź też, czy teksty po trim nie są puste. Samo !Number.isNaN nie odrzuca Infinity ani zera.
Zapisz w zmiennej liczbę całkowitą (np. 202) i wypisz ją w czterech postaciach:
dziesiętnej, dwójkowej, ósemkowej i szesnastkowej. Skorzystaj z toString(podstawa).
Następnie w drugą stronę: zamień tekst "1101" traktowany jako liczba dwójkowa
na wartość dziesiętną — przyda się drugi argument parseInt.
pokaż rozwiązanie
const liczba = 202;
console.log("DEC: " + liczba.toString(10)); // 202
console.log("BIN: " + liczba.toString(2)); // 11001010
console.log("OCT: " + liczba.toString(8)); // 312
console.log("HEX: " + liczba.toString(16)); // ca
const binarnie = "1101";
const dziesietnie = parseInt(binarnie, 2);
console.log(binarnie + " (2) = " + dziesietnie + " (10)"); // 13
Podawaj podstawę jawnie, aby zamiar był czytelny. Współczesne parseInt(„010”) daje 10, a prefiks 0x może oznaczać zapis szesnastkowy. parseInt odczytuje początkowy fragment, więc parseInt(„12abc”, 10) daje 12 — to nie walidacja całego tekstu.
Jeszcze dwa typy do rozpoznania
bigint, np. 123n, opisuje liczby całkowite poza ograniczeniem dokładności number; nie mieszaj go bezpośrednio z number w arytmetyce. symbol pozwala tworzyć unikalne klucze. Nie potrzebujemy ich w pierwszych zadaniach, ale nie są odmianami string ani number.
Co trzeba zapamiętać
constdomyślnie,lettylko gdy wartość musi się zmieniać,varnigdy.- Typ podaje się po dwukropku, ale przy podanej wartości początkowej zwykle nie trzeba go pisać.
- Typ ustala się w momencie deklaracji i później się nie zmienia — nawet gdy go nie zapisałeś.
- Jeden typ
numberobsługuje liczby całkowite i dziesiętne;7 / 2to3.5. Number(…),parseInt(…, 10),String(…),toFixed(n)— podstawowe zamiany typów.- Nieudana zamiana daje
NaN; wykrywasz ją wyłącznie przezNumber.isNaN(…).