30 marca 2023 7 min. czytania

Programowanie obiektowe w języku TypeScript

Programowanie obiektowe jest podstawą wielu języków programowania. Dlatego zapoznamy się ze składnią OOP w języku TypeScript i porównamy ją z JavaScriptem.

Zaktualizowano: 30 marca 2023
Wiele białych filarów w dwóch rzędach
Zdjęcie autorstwa Chris Brignola

W ostatnim wpisie przedstawiłem przesłanki stojące za używaniem TypeScripta. Krótko go opisałem i wymieniłem podstawowe typy, przydatne podczas codziennej pracy z oprogramowaniem. Ale podstawowe typy to za mało, żeby budować współczesne, złożone aplikacje webowe.

W tym poście zakładam, że rozumiesz OOP w czystym języku JavaScript. Nie jesteś pewien co do swojej wiedzy? Nie przejmuj się, mam coś dla ciebie - Programowanie obiektowe w języku JavaScript.

Aby modelować rzeczywiste przedmioty, potrzebujemy obiektów. I klas. I interfejsów. W tym wpisie, poznamy je wszystkie. Pod koniec tego wpisu, powinniśmy dobrze rozumieć OOP w języku TypeScript. Powinniśmy poznać najważniejszą składnię i jak wypada w porównaniu do OOP w języku JavaScript.

Klasa

Definiowanie klas w TypeScripcie jest identycznie jak w “waniliowym” języku (przynajmniej w ECMAScript 6). Dostępne jest słowo kluczowe class, które możesz wykorzystać do tego celu. Jednakże, w przypadku pól, mamy więcej opcji. TypeScript oferuje modyfikatory, które zmieniają widoczność poszczególnych właściwości.

KlasyTYPESCRIPT
1class Computer {
2 // W czystym języku JavaScript nie ma słów "private" lub "public".
3 private name: string
4 constructor(name) {
5 this.name = name
6 }
7 // Moyfikator "public" jest domyślny, ale możeszz go jawnie zapisać.
8 public info(this: Computer) {
9 console.log(`Name of the computer: ${this.name}`)
10 }
11}
12
13const desktop = new Computer('Mac Studio')
14const laptop = new Computer('Macbook Pro')

Czy wiedziałeś, że prywatne funkcjonalności klas są dostępne także w czystym JavaScripcie? ECMAScript 2022 wprowadził prywatne pola, metody i akcesory. Poprzedź nazwę właściwości znakiem krzyżyka (#), aby użyć tej nowej składni. TypeScript 3.8 (i wyższe wersje) także wspierają tę nową składnię JavaScriptu dla prywatnych pól.

Właściwość jest zapisana dwukrotnie w powyższym fragmencie kodu. TypeScript oferuje skrót, który pozwala unikać takich duplikacji.

Skrót do definiowania właściwościTYPESCRIPT
1class Computer {
2 constructor(private name: string) {}
3 // Jest to wskazówka dla TypeScripta do czego odnosi się "this".
4 public info(this: Computer) {
5 console.log(`Name of the computer: ${this.name}`)
6 }
7}

Dziedziczenie

Jeżeli wiesz jak działa dziedziczenie w JavaScripcie, TypeScript nie zaskoczy cię (no dobra, może trochę). Klasy dziedziczą po sobie za pomocą słówka extends. Istnieje także modyfikator powiązany z dziedziczeniem - protected. Pola oznaczone jako “protected” są dostępne wewnątrz klasy, a także wewnątrz każdej dziedziczącej klasy. Poniższa tabela porównuje modyfikatory pól w TypeScripcie.

ModyfikatorKlasaDziedziczące klasyPozostały kod
PublicDostępneDostępneDostępne
ProtectedDostępneDostępneNiedostępne
PrivateDostępneNiedostępneNiedostępne

Poza wymienionymi modyfikatorami, istnieje inny sposób zabezpieczania pól w języku TypeScript. Słowo kluczowe readonly robi to co nazwa wskazuje - oznacza właściwości jako tylko do odczytu. Nie możesz ich zmienić po inicjalizacji.

Klasy i dziedziczenieTYPESCRIPT
1class Computer {
2 constructor(
3 protected name: string,
4 protected readonly id: string
5 ) {}
6 public info(this: Computer) {
7 console.log(`Name of the computer: ${this.name}`)
8 }
9}
10
11class Desktop extends Computer {
12 private gpu: string
13 constructor(name: string, id: string, gpu: string) {
14 super(name, id)
15 this.gpu = gpu
16 }
17}
18
19class Laptop extends Computer {
20 private display: string
21 constructor(name: string, id: string, display: string) {
22 super(name, id)
23 this.display = display
24 }
25}
26
27const desktop = new Desktop('Mac Studio', 'sdag89', 'AMD Radeon Pro W6800X')
28const laptop = new Laptop('Macbook Air', 'bzkx32', '13.6"')

Statyczne pola

Do tej pory mówiliśmy o metodach i właściwościach instancji. Ale istnieje także sposób na dostęp do właściwości i metod bezpośrednio na klasie. Słowo kluczowe static definiuje statyczną metodę lub pole klasy. Działa zarówno w języku JavaScript jak i TypeScript. Najprawdopodobniej już miałeś okazję używać statycznych metod/właściwości. Wszystkie właściwości i metody obiektu Math - jak Math.PI lub Math.abs() - są statyczne.

Statyczne polaTYPESCRIPT
1class Computer {
2 static firstProgrammer = 'Ada Lovelace'
3}
4
5console.log(Computer.firstProgrammer) // Ada Lovelace

Abstrakcyjna klasa

Czasami chcesz mieć pewnosć, że klasa zaimplementowała jakąś metodę. Klasy dziedziczą metody, ale nie możesz zapewnić domyślnej implementacji. Chcesz wymusić na programistach pracujących z klasą, stworzenie swojej własnej wersji. W takich wypadkach przydatna jest klasa abstrakcyjna. TypeScript oferuje słowo kluczowe abstract, które możesz wykorzystać z klasami i metodami.

Abstrakcyjne klasyTYPESCRIPT
1abstract class Computer {
2 abstract info(): void
3}
4
5class Desktop extends Computer {
6 constructor(private gpu: string) {
7 super()
8 }
9 info(): void {
10 console.log(`Desktop gpu: ${this.gpu}`)
11 }
12}
13
14class Laptop extends Computer {
15 constructor(private display: string) {
16 super()
17 }
18 info(): void {
19 console.log(`Laptop display: ${this.display}`)
20 }
21}
22
23const desktop = new Desktop('AMD Radeon Pro W6800X')
24const laptop = new Laptop('16"')
25
26desktop.info() // Desktop GPU: AMD Radeon Pro W6800X
27laptop.info() // Laptop display: 16"

Interfejs

Prosto rzecz ujmując, interfejs opisuje strukturę obiektu. Pod tym względem jest podobny do klasy abstrakcyjnej ale nie taki sam. Klasa abstrakcyjna może zapewnić również implementację. Interfejs definiuje tylko strukturę. Nie jest to schemat jak klasa. Bardziej przypomina niestandardowy typ. Możesz wykorzystywać interfejsy i niestandardowe typy zamiennie, ale one też nie są identyczne. Interfejsy są tylko dla obiektów. Niestandardowe typy są bardziej elastyczne i mogą przechowywać inne rzeczy, takie jak unie. Jaki jest zatem cel interfejsu? Jest jaśniejszy. Możesz być pewien, że pracujesz z obiektem wykorzystując interfejs.

Interfejsy i typyTYPESCRIPT
1interface Computer {
2 name: string
3 readonly id: string
4 info(): void
5}
6
7// Możesz zastąpić interfejs typem.
8type ComputerType = {
9 name: string
10 readonly id: string
11 info(): void
12}
13
14let desktop: Computer
15desktop = {
16 name: 'Mac Studio',
17 id: 'gdd89s',
18 info() {
19 console.log(`Name of the computer: ${this.name}`)
20 }
21}
22
23desktop.info()

Klasa może zaimplementować interfejs i wykorzystać go jak kontrakt. Istnieje słowo kluczowe implements do tego celu. W przeciwieństwie do dziedziczenia, klasa może implementować wiele interfejsów. Interfejs mogą rozszerzać się wzajemnie. Daje ci to dużo mozliwości podczas kompozycji i ponownego wykorzystania kodu.

Interfejsy i klasyTYPESCRIPT
1interface Named {
2 name: string
3}
4
5interface Informed {
6 info(): void
7}
8
9class Computer implements Named, Informed {
10 name: string
11 constructor(name: string) {
12 this.name = name
13 }
14 info(): void {
15 console.log(`Name of the computer: ${this.name}`)
16 }
17}
18
19const laptop = new Computer('MacBook Pro')
20laptop.info()

Możesz także użyć interfejsów do zdefiniowania struktury funkcji. “Zaraz, zaraz, czy nie wspominałeś, że można ich tylko użyć z obiektami?”. Tak i nie ma tu sprzeczności, bo funkcje JavaScript są obiektami pierwszej klasy. Tak, JS ma swoje arkana. W każdym razie, możesz użyć interfejsu do zastąpienia typów funkcji. Jest to rzadziej spotykany zapis, ale masz taką alternatywę.

Interfejsy i funkcjeTYPESCRIPT
1type Info = (name: string) => void
2
3interface InfoInterface {
4 (name: string): void
5}
6
7const info: InfoInterface = (name: string) => {
8 console.log(`Name of the computer: ${name}`)
9}

Spędziłem trochę czasu opisując interfejsy, ale na koniec dnia (lub raczej kompilacji), interfejsów nie ma w kodzie JavaScript. Jest to czysto deweloperska funkcjonalność wprowadzona przez TypeScript.

Interfejsy po kompilacjiJAVASCRIPT
1var info = function (name) {
2 console.log('Name of the computer: '.concat(name))
3}

Tak jak interfejsy znikają podczas kompilacji, ja znikam po opisaniu ich. Chcę żeby te posty o TypeScripcie można było łatwo przetrawić, więc tu zakończę. Oczywiście, jest o wiele więcej tematów dotyczących TypeScripta. Bardziej złożonym tematom, jak generyki czy dekoratory, przyjrzymy się w następnych postach. Jeżeli chcesz poczytać o mniej złożonych konceptach, sprawdź mój ostatni wpis - TypeScript - podstawowe typy.

Newsletter, który rozpala ciekawość💡

Subskrybuj mój newsletter, aby otrzymywać comiesięczną dawkę:

  • Nowości, przykładów, inspiracji ze świata front-end, programowania i designu
  • Teorii naukowych i sceptycyzmu
  • Moich ulubionych źródeł, idei, narzędzi i innych interesujących linków
Nie jestem nigeryjskim księciem, aby oferować ci okazje. Nie wysyłam spamu. Anuluj kiedy chcesz.

Pozostań ciekawy. Przeczytaj więcej

Połowa winyla na białym tle1 września 20227 min. czytania

Dostępne animacje w React

Czyli jak nie kręcić swoimi użytkownikami (jak winylem). Niektóre animacje mogą powodować problemy u użytkoników. Zadbamy o nich i sprawimy, że nieistotne animacje będą opcjonalne.

Czytaj wpis
Zdjęcie zmiennych CSS w edytorze Visual Studio Code14 września 20227 min. czytania

Konwertowanie tokenów projektowych na zmienne CSS z Node.js

Konwertowanie tokenów projektowych jest procesem podatnym na błędy - przekonałem się o tym na własnej skórze. Dlatego stworzyłem prosty skrypt dla środowiska Node.js, który pomoże mi z tym zadaniem.

Czytaj wpis
Pięć metalowych kół zębatych na czarnym tle23 września 202210 min. czytania

Gatsby z Netlify CMS

W tym wpisie przyjrzymy się bliżej Netlify CMS. Jest to przykład CMSa nowego typu, który jest oparty o Git. Zintegrujemy go z przykładowym projektem Gatsby.

Czytaj wpis