Menu

Typy obiektowe w TypeScript: właściwości opcjonalne i readonly

Jak typować obiekty w TypeScript: typy obiektowe inline, właściwości opcjonalne z ?, właściwości readonly, zagnieżdżone obiekty, metody, sprawdzanie nadmiarowych właściwości oraz różnica między object, {} i Object.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Typ obiektowy w TypeScript wymienia właściwości obiektu i typ każdej z nich: { name: string; age: number }. Dodaj ?, żeby właściwość była opcjonalna, i readonly, żeby zablokować ponowne przypisanie. Zapisz typ inline albo nazwij go przez type lub interface, żeby używać go wielokrotnie.

Zapisywanie typów obiektowych

Właściwości oddziela się ; lub , (działają oba, ; to zwyczajowy styl), a sam znak nowej linii też wystarcza. Typ inline sprawdza się przy jednorazowym parametrze; wszystko, czego używasz dwa razy, warto nazwać.

// Inline, in a parameter
function area(rect: { width: number; height: number }): number {
    return rect.width * rect.height;
}

// Named with a type alias
type Rect = { width: number; height: number };

// Named with an interface (the same shape)
interface RectShape {
    width: number;
    height: number;
}

type i interface równie dobrze opisują kształty obiektów. Różnice (łączenie deklaracji, unie) omawia strona interface vs type.

Dostęp do właściwości, której typ nie deklaruje, to błąd kompilacji: point.z daje TS2339, Property 'z' does not exist on type '{ x: number; y: number; }'.

Właściwości opcjonalne

? po nazwie pozwala pominąć właściwość. Odczyt właściwości opcjonalnej daje T | undefined, więc TypeScript każe ci obsłużyć przypadek braku, zanim jej użyjesz.

Wywołanie metody na właściwości opcjonalnej bez sprawdzenia to błąd: p.nickname.toUpperCase() kończy się TS18048, 'p.nickname' is possibly 'undefined'. Użyj optional chaining (p.nickname?.toUpperCase()), gdy undefined to akceptowalny wynik.

prop?: T i prop: T | undefined to nie to samo. Pierwsze pozwala, by klucza nie było; drugie wymaga klucza, choć jego wartość może być undefined:

Właściwości readonly

readonly blokuje ponowne przypisanie właściwości po utworzeniu obiektu. To tylko sprawdzenie podczas kompilacji i jest płytkie: obiekt lub tablicę zapisaną we właściwości readonly nadal można zmieniać od środka.

Wynik pokazuje oba ograniczenia: id naprawdę zmieniło się w czasie działania (tylko kompilator wiedział, że jest readonly), a tablica w środku została zmodyfikowana. Dla tablicy tylko do odczytu użyj readonly string[]; żeby wszystkie właściwości naraz były readonly, użyj Readonly<Order>.

Sprawdzanie nadmiarowych właściwości

Gdy przypisujesz literał obiektu bezpośrednio do otypowanej zmiennej albo przekazujesz go wprost do otypowanego parametru, TypeScript odrzuca każdą właściwość, której typ nie deklaruje. Dodatkowe właściwości w świeżym literale to prawie zawsze literówki.

index.ts(8,8): error TS2561: Object literal may only specify known properties, but 'colour' does not exist in type 'Options'. Did you mean to write 'color'?

Kod błędu to TS2561, bo kompilator znalazł bliskie dopasowanie; dodatkowa właściwość bez podobnej nazwy daje TS2353, Object literal may only specify known properties, and 'z' does not exist in type 'Point'. Bez tego sprawdzenia literówka by się skompilowała, color byłoby undefined, a program po cichu rysowałby na czarno. Sprawdzenie dotyczy tylko świeżych literałów. Obiekt, który już jest w zmiennej, może mieć dodatkowe właściwości, bo typowanie w TypeScript jest strukturalne: wartość pasuje do typu, gdy ma co najmniej wymagane właściwości.

Zagnieżdżone obiekty i metody

Typy obiektowe można zagnieżdżać, a metody opisuje się składnią metody albo właściwością o typie funkcji.

Przy głębokich lub wielokrotnie używanych kształtach nazwij typ wewnętrzny (type Address = { ... }) i odwołuj się do niego albo wyciągnij go z typu zewnętrznego przez indexed access, Company["address"], jak w ostatnich liniach.

object a {} a Object

Trzy typy brzmią podobnie, a znaczą co innego:

TypPrzyjmujeOdrzuca
objectdowolną wartość nieprymitywną: {}, [], funkcje, instancje klas5, "a", true, null, undefined
{}dowolną wartość poza null i undefined, łącznie z prymitywaminull, undefined
Objectto samo co {}, plus sprawdzenie, czy wbudowane składowe, takie jak toString, zachowują zgodne typynull, undefined
{ x: number }dowolną wartość z liczbowym xwartości bez x

{} nie oznacza "pustego obiektu"; oznacza "nie null i nie undefined". Aby przyjąć dowolny obiekt o nieznanych kluczach, użyj Record<string, unknown>; dla mapy kluczy na wartości użyj sygnatury indeksu albo Record, jak na stronie dictionary. W większości przypadków konkretny kształt jest lepszy niż każdy z tych trzech.

Najczęściej zadawane pytania

Jak zdefiniować typ obiektu w TypeScript?

Wypisz właściwości i ich typy w nawiasach klamrowych: { name: string; age: number }. Możesz zapisać go inline w adnotacji albo nadać mu nazwę przez type User = { ... } lub interface User { ... } i używać ponownie. Właściwości oddzielasz ; lub ,.

Jak zrobić właściwość opcjonalną w TypeScript?

Postaw ? po nazwie właściwości: { name: string; nickname?: string }. Obiekt może pominąć nickname, a odczyt daje string | undefined, więc musisz to sprawdzić albo podać wartość domyślną (user.nickname ?? user.name), zanim użyjesz jej jako stringa.

Czym różni się prop?: string od prop: string | undefined?

Przy prop?: string klucz można całkiem pominąć. Przy prop: string | undefined klucz jest wymagany, choć jego wartość może być undefined, więc {} to błąd kompilacji. Odczyt w obu przypadkach daje string | undefined.

Czym różnią się object, {} i Object w TypeScript?

object oznacza dowolną wartość nieprymitywną (obiekty, tablice, funkcje) i odrzuca 5 czy "a". {} oznacza dowolną wartość poza null i undefined, łącznie z prymitywami. Object to prawie to samo co {}, ale dodatkowo sprawdza, czy wbudowane metody, takie jak toString, zachowują zgodne typy. Używaj object albo, jeszcze lepiej, konkretnego kształtu zamiast {} czy Object.

Dlaczego TypeScript zgłasza, że literał obiektu może zawierać tylko znane właściwości?

To sprawdzanie nadmiarowych właściwości (excess property check): błąd TS2353 albo TS2561, gdy kompilator potrafi podsunąć właściwość, o którą prawdopodobnie chodziło. Gdy przypisujesz świeży literał obiektu bezpośrednio do otypowanej zmiennej lub parametru, każda właściwość, której typ nie deklaruje, zostaje zgłoszona, bo zwykle to literówka. Przypisanie obiektu zapisanego w innej zmiennej pomija to sprawdzenie, bo typowanie strukturalne dopuszcza dodatkowe właściwości.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ