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 2023W 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: string4 constructor(name) {5 this.name = name6 }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}1213const 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.
Modyfikator | Klasa | Dziedziczące klasy | Pozostały kod |
---|---|---|---|
Public | Dostępne | Dostępne | Dostępne |
Protected | Dostępne | Dostępne | Niedostępne |
Private | Dostępne | Niedostępne | Niedostę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: string5 ) {}6 public info(this: Computer) {7 console.log(`Name of the computer: ${this.name}`)8 }9}1011class Desktop extends Computer {12 private gpu: string13 constructor(name: string, id: string, gpu: string) {14 super(name, id)15 this.gpu = gpu16 }17}1819class Laptop extends Computer {20 private display: string21 constructor(name: string, id: string, display: string) {22 super(name, id)23 this.display = display24 }25}2627const 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}45console.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(): void3}45class Desktop extends Computer {6 constructor(private gpu: string) {7 super()8 }9 info(): void {10 console.log(`Desktop gpu: ${this.gpu}`)11 }12}1314class Laptop extends Computer {15 constructor(private display: string) {16 super()17 }18 info(): void {19 console.log(`Laptop display: ${this.display}`)20 }21}2223const desktop = new Desktop('AMD Radeon Pro W6800X')24const laptop = new Laptop('16"')2526desktop.info() // Desktop GPU: AMD Radeon Pro W6800X27laptop.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: string3 readonly id: string4 info(): void5}67// Możesz zastąpić interfejs typem.8type ComputerType = {9 name: string10 readonly id: string11 info(): void12}1314let desktop: Computer15desktop = {16 name: 'Mac Studio',17 id: 'gdd89s',18 info() {19 console.log(`Name of the computer: ${this.name}`)20 }21}2223desktop.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: string3}45interface Informed {6 info(): void7}89class Computer implements Named, Informed {10 name: string11 constructor(name: string) {12 this.name = name13 }14 info(): void {15 console.log(`Name of the computer: ${this.name}`)16 }17}1819const 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) => void23interface InfoInterface {4 (name: string): void5}67const 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.