Paweł Łukasiewicz
2024-12-02
Paweł Łukasiewicz
2024-12-02
Udostępnij Udostępnij Kontakt
Wprowadzenie

TypeScript dostarcza wiele wbudowanych typów użytkowych (Utility Types), które pomagają w codziennej pracy z typami i obiektami. Są to typy, które pozwalają na modyfikowanie lub wyciąganie typów na różne sposoby, co zwiększa elastyczność i ułatwia pracę z bardziej złożonymi strukturami danych.

W tej sekcji omówimy najczęściej używane typy użytkowe, takie jak Partial, Readonly, oraz Pick.

Typ Partial

Partial<T> jest typem użytkowym, który pozwala na tworzenie wersji typu T, w której wszystkie właściwości są opcjonalne. Jest to przydatne w sytuacjach, gdy nie chcemy, aby wszystkie pola w obiekcie były obowiązkowe, np. przy aktualizacji tylko niektórych właściwości w obiekcie.

Przykład: Załóżmy, że mamy interfejs User, który określa strukturę obiektu użytkownika:

interface User { 
    name: string; 
    age: number; 
    email: string; 
}
Domyślnie wszystkie właściwości są wymagane. Jeśli jednak chcemy stworzyć funkcję, która aktualizuje tylko niektóre właściwości użytkownika, możemy użyć Partial:
function updateUser(user: User, updates: Partial<User>): User {
    return { ...user, ...updates };
}

let user: User = { name: "Jan", age: 30, email: "jan@example.com" };
let updatedUser = updateUser(user, { email: "jan.nowy@example.com" });

console.log(updatedUser);
// Zwraca: { name: "Jan", age: 30, email: "jan.nowy@example.com" }
W tym przykładzie, Partial<User> pozwala nam na przekazanie obiektu z opcjonalnymi właściwościami, takimi jak email, bez konieczności podawania pozostałych pól.

Typ Readonly

Typ Readonly przekształca wszystkie właściwości typu T w tylko do odczytu (readonly). Oznacza to, że raz przypisana wartość nie może być zmieniona. Przydaje się to w sytuacjach, gdy chcemy zapewnić niezmienność obiektów i chronić je przed przypadkowymi modyfikacjami.

Przykład:

interface Car {
    brand: string;
    model: string;
    year: number;
}

const myCar: Readonly<Car> = {
    brand: "Toyota",
    model: "Corolla",
    year: 2021,
};

// myCar.brand = "Honda"; // Błąd, ponieważ 'brand' jest readonly
Tutaj Readonly<Car> sprawia, że obiekt myCar staje się niezmienny – żadna z jego właściwości nie może być nadpisana.

Typ Pick

Pick<T, K> pozwala na wybranie kilku właściwości z istniejącego typu i stworzenie nowego typu, który zawiera tylko te właściwości. Jest to przydatne, gdy chcemy stworzyć nowy typ z podzbiorem właściwości innego typu.

Przykład:

interface Employee {
    id: number;
    name: string;
    position: string;
    salary: number;
}

type EmployeeOverview = Pick<Employee, "id" | "name">;

let employee: EmployeeOverview = {
    id: 1,
    name: "Jan Nowak",
};

W tym przykładzie Pick<Employee, "id" | "name"> tworzy nowy typ EmployeeOverview, który zawiera tylko właściwości id i name z typu Employee. Dzięki temu możemy pracować z podzbiorem właściwości, co często bywa użyteczne w dużych projektach.

Typ Omit

Typ Omit<T, K> działa odwrotnie do Pick – pozwala na usunięcie wybranych właściwości z istniejącego typu. Jest to przydatne, gdy chcemy pozbyć się pewnych właściwości, których nie potrzebujemy w danym kontekście.

Przykład:

type EmployeeWithoutSalary = Omit<Employee, "salary">;

let employee: EmployeeWithoutSalary = {
    id: 1,
    name: "Jan Nowak",
    position: "Developer",
};

W tym przypadku Omit<Employee, "salary"> tworzy nowy typ EmployeeWithoutSalary, który zawiera wszystkie właściwości z wyjątkiem salary.

Typ Record

Typ Record<K, T> pozwala na stworzenie obiektu, w którym klucze są typu K, a wartości są typu T. Jest to przydatne, gdy chcemy stworzyć obiekt, który będzie przechowywał wartości o jednolitym typie, a klucze będą reprezentować określony typ.

Przykład:

type ProductCategories = "electronics" | "clothing" | "groceries";

let productInventory: Record<ProductCategories, number> = {
    electronics: 100,
    clothing: 50,
    groceries: 200,
};

Tutaj Record<ProductCategories, number> tworzy obiekt, w którym klucze to nazwy kategorii produktów ("electronics", "clothing", "groceries"), a wartości to liczby, reprezentujące ilość produktów w każdej kategorii.

Typ Required

Typ Required<T> zmienia wszystkie właściwości obiektu na obowiązkowe (czyli usuwa ewentualne oznaczenie opcjonalności ?). Jest to przydatne w sytuacjach, gdzie chcemy mieć pewność, że wszystkie pola zostaną uzupełnione.

Przykład:

interface User {
    name?: string;
    age?: number;
}

let requiredUser: Required<User> = {
    name: "Anna",
    age: 25,
};
// Teraz zarówno 'name', jak i 'age' są wymagane
Required<User> przekształca wszystkie opcjonalne właściwości w obowiązkowe, co oznacza, że musimy teraz podać zarówno name, jak i age.

Typ NonNullable

Typ NonNullable<T> usuwa z typu T wartości null oraz undefined. Jest to przydatne, gdy chcemy upewnić się, że dana wartość nie będzie pusta.

Przykład:

type Name = string | null | undefined;
let validName: NonNullable<Name> = "Jan";
// validName nie może być null ani undefined
W tym przykładzie NonNullable<Name> tworzy typ, który pozwala tylko na wartości typu string, eliminując null i undefined.

Podsumowanie

Typy użytkowe w TypeScript, takie jak Partial, Readonly, Pick, czy Omit, są niezwykle przydatne, ponieważ pozwalają na tworzenie bardziej elastycznego i zwięzłego kodu, który jest łatwiejszy do utrzymania i mniej podatny na błędy. Te narzędzia dają możliwość modyfikowania istniejących typów w różnych kierunkach, co ułatwia pracę z bardziej złożonymi strukturami danych. Dzięki nim możesz w łatwy sposób definiować typy, które są dostosowane do potrzeb twojego projektu, co czyni kod bardziej skalowalnym i zrozumiałym.