Eesti

Avastage TypeScript'i utiliittüüpide võimsus, et kirjutada puhtamat, paremini hooldatavat ja tüübikindlat koodi. Uurige praktilisi rakendusi reaalsete näidetega.

TypeScript'i utiliittüüpide valdamine: praktiline juhend globaalsetele arendajatele

TypeScript pakub võimsat komplekti sisseehitatud utiliittüüpe, mis võivad oluliselt parandada teie koodi tüübikindlust, loetavust ja hooldatavust. Need utiliittüübid on sisuliselt eelnevalt määratletud tüübimuutused, mida saate olemasolevatele tüüpidele rakendada, säästes teid korduva ja vigaderohke koodi kirjutamisest. See juhend uurib erinevaid utiliittüüpe praktiliste näidetega, mis on mõistetavad arendajatele üle kogu maailma.

Miks kasutada utiliittüüpe?

Utiliittüübid lahendavad levinud tüübimanipulatsiooni stsenaariume. Neid kasutades saate:

Põhilised utiliittüübid

Partial<T>

Partial<T> loob tüübi, kus kõik T omadused on muudetud valikulisteks. See on eriti kasulik, kui soovite luua tüüpi osaliste uuenduste või konfiguratsiooniobjektide jaoks.

Näide:

Kujutage ette, et ehitate e-kaubanduse platvormi klientidele erinevatest piirkondadest. Teil on Customer tüüp:


interface Customer {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  phoneNumber: string;
  address: {
    street: string;
    city: string;
    country: string;
    postalCode: string;
  };
  preferences?: {
    language: string;
    currency: string;
  }
}

Kliendi teabe uuendamisel ei pruugi te nõuda kõiki välju. Partial<Customer> võimaldab teil määratleda tüübi, kus kõik Customer omadused on valikulised:


type PartialCustomer = Partial<Customer>;

function updateCustomer(id: string, updates: PartialCustomer): void {
  // ... implementatsioon kliendi uuendamiseks antud ID-ga
}

updateCustomer("123", { firstName: "John", lastName: "Doe" }); // Kehtiv
updateCustomer("456", { address: { city: "London" } }); // Kehtiv

Readonly<T>

Readonly<T> loob tüübi, kus kõik T omadused on muudetud kirjutuskaitstuks (readonly), vältides muutmist pärast lähtestamist. See on väärtuslik muutumatuse tagamiseks.

Näide:

Mõelge oma globaalse rakenduse konfiguratsiooniobjektile:


interface AppConfig {
  apiUrl: string;
  theme: string;
  supportedLanguages: string[];
  version: string; // Lisatud versioon
}

const config: AppConfig = {
  apiUrl: "https://api.example.com",
  theme: "dark",
  supportedLanguages: ["en", "fr", "de", "es", "zh"],
  version: "1.0.0"
};

Et vältida konfiguratsiooni juhuslikku muutmist pärast lähtestamist, saate kasutada Readonly<AppConfig>:


type ReadonlyAppConfig = Readonly<AppConfig>;

const readonlyConfig: ReadonlyAppConfig = {
  apiUrl: "https://api.example.com",
  theme: "dark",
  supportedLanguages: ["en", "fr", "de", "es", "zh"],
  version: "1.0.0"
};

// readonlyConfig.apiUrl = "https://newapi.example.com"; // Viga: Ei saa omistada 'apiUrl'-ile, kuna see on kirjutuskaitstud omadus.

Pick<T, K>

Pick<T, K> loob tüübi, valides T-st omaduste hulga K, kus K on string-literaalide tüüpide unioon, mis esindavad omaduste nimesid, mida soovite kaasata.

Näide:

Oletame, et teil on Event liides erinevate omadustega:


interface Event {
  id: string;
  title: string;
  description: string;
  location: string;
  startTime: Date;
  endTime: Date;
  organizer: string;
  attendees: string[];
}

Kui vajate konkreetse kuvakomponendi jaoks ainult title, location ja startTime omadusi, saate kasutada Pick:


type EventSummary = Pick<Event, "title" | "location" | "startTime">;

function displayEventSummary(event: EventSummary): void {
  console.log(`Sündmus: ${event.title} asukohas ${event.location} kell ${event.startTime}`);
}

Omit<T, K>

Omit<T, K> loob tüübi, jättes välja T-st omaduste hulga K, kus K on string-literaalide tüüpide unioon, mis esindavad omaduste nimesid, mida soovite välistada. See on vastupidine Pick'ile.

Näide:

Kasutades sama Event liidest, kui soovite luua tüüpi uute sündmuste loomiseks, võiksite välistada id omaduse, mille tavaliselt genereerib taustaprogramm (backend):


type NewEvent = Omit<Event, "id">;

function createEvent(event: NewEvent): void {
  // ... implementatsioon uue sündmuse loomiseks
}

Record<K, T>

Record<K, T> loob objektitüübi, mille omaduste võtmed on K ja mille omaduste väärtused on T. K võib olla string-literaalide tüüpide, number-literaalide tüüpide või sümboli unioon. See sobib ideaalselt sõnastike või kaartide loomiseks.

Näide:

Kujutage ette, et peate salvestama oma rakenduse kasutajaliidese tõlkeid. Saate kasutada Record'i, et määratleda oma tõlgete tüüp:


type Translations = Record<string, string>;

const enTranslations: Translations = {
  "hello": "Hello",
  "goodbye": "Goodbye",
  "welcome": "Welcome to our platform!"
};

const frTranslations: Translations = {
  "hello": "Bonjour",
  "goodbye": "Au revoir",
  "welcome": "Bienvenue sur notre plateforme !"
};

function translate(key: string, language: string): string {
  const translations = language === "en" ? enTranslations : frTranslations; //Lihtsustatud
  return translations[key] || key; // Tagastab võtme, kui tõlget ei leita
}

console.log(translate("hello", "en")); // Väljund: Hello
console.log(translate("hello", "fr")); // Väljund: Bonjour
console.log(translate("nonexistent", "en")); // Väljund: nonexistent

Exclude<T, U>

Exclude<T, U> loob tüübi, välistades T-st kõik uniooni liikmed, mis on omistatavad U-le. See on kasulik konkreetsete tüüpide väljafiltreerimiseks unioonist.

Näide:

Teil võib olla tüüp, mis esindab erinevaid sündmuste tüüpe:


type EventType = "concert" | "conference" | "workshop" | "webinar";

Kui soovite luua tüübi, mis välistab "webinar" sündmused, saate kasutada Exclude'i:


type PhysicalEvent = Exclude<EventType, "webinar">;

// PhysicalEvent on nüüd "concert" | "conference" | "workshop"

function attendPhysicalEvent(event: PhysicalEvent): void {
  console.log(`Osaleb füüsilisel sündmusel ${event}`);
}

// attendPhysicalEvent("webinar"); // Viga: Tüübi '"webinar"' argumenti ei saa määrata parameetrile tüübiga '"concert" | "conference" | "workshop"'.

attendPhysicalEvent("concert"); // Kehtiv

Extract<T, U>

Extract<T, U> loob tüübi, eraldades T-st kõik uniooni liikmed, mis on omistatavad U-le. See on vastupidine Exclude'ile.

Näide:

Kasutades sama EventType'i, saate eraldada webinar'i sündmuse tüübi:


type OnlineEvent = Extract<EventType, "webinar">;

// OnlineEvent on nüüd "webinar"

function attendOnlineEvent(event: OnlineEvent): void {
  console.log(`Osaleb veebisündmusel ${event}`);
}

attendOnlineEvent("webinar"); // Kehtiv
// attendOnlineEvent("concert"); // Viga: Tüübi '"concert"' argumenti ei saa määrata parameetrile tüübiga '"webinar"'.

NonNullable<T>

NonNullable<T> loob tüübi, välistades T-st null ja undefined.

Näide:


type MaybeString = string | null | undefined;

type DefinitelyString = NonNullable<MaybeString>;

// DefinitelyString on nüüd string

function processString(str: DefinitelyString): void {
  console.log(str.toUpperCase());
}

// processString(null); // Viga: Tüübi 'null' argumenti ei saa määrata parameetrile tüübiga 'string'.
// processString(undefined); // Viga: Tüübi 'undefined' argumenti ei saa määrata parameetrile tüübiga 'string'.
processString("hello"); // Kehtiv

ReturnType<T>

ReturnType<T> loob tüübi, mis koosneb funktsiooni T tagastustüübist.

Näide:


function greet(name: string): string {
  return `Hello, ${name}!`;
}

type Greeting = ReturnType<typeof greet>;

// Greeting on nüüd string

const message: Greeting = greet("World");

console.log(message);

Parameters<T>

Parameters<T> loob korteežitüübi funktsioonitüübi T parameetrite tüüpidest.

Näide:


function logEvent(eventName: string, eventData: object): void {
  console.log(`Sündmus: ${eventName}`, eventData);
}

type LogEventParams = Parameters<typeof logEvent>;

// LogEventParams on nüüd [eventName: string, eventData: object]

const params: LogEventParams = ["user_login", { userId: "123", timestamp: Date.now() }];

logEvent(...params);

ConstructorParameters<T>

ConstructorParameters<T> loob korteeži- või massiivitüübi konstruktorfunktsiooni tüübi T parameetrite tüüpidest. See järeldab argumentide tüübid, mis tuleb edastada klassi konstruktorile.

Näide:


class Greeter {
  greeting: string;

  constructor(message: string) {
    this.greeting = message;
  }

  greet() {
    return "Hello, " + this.greeting;
  }
}


type GreeterParams = ConstructorParameters<typeof Greeter>;

// GreeterParams on nüüd [message: string]

const paramsGreeter: GreeterParams = ["World"];
const greeterInstance = new Greeter(...paramsGreeter);

console.log(greeterInstance.greet()); // Väljund: Hello, World

Required<T>

Required<T> loob tüübi, mis koosneb kõigist T omadustest, mis on määratud kohustuslikuks. See muudab kõik valikulised omadused kohustuslikuks.

Näide:


interface UserProfile {
  name: string;
  age?: number;
  email?: string;
}

type RequiredUserProfile = Required<UserProfile>;

// RequiredUserProfile on nüüd { name: string; age: number; email: string; }

const completeProfile: RequiredUserProfile = {
  name: "Alice",
  age: 30,
  email: "alice@example.com"
};

// const incompleteProfile: RequiredUserProfile = { name: "Bob" }; // Viga: Omadus 'age' on puudu tüübis '{ name: string; }', kuid on nõutud tüübis 'Required'.

Täpsemad utiliittüübid

Mall-literaalide tüübid

Mall-literaalide tüübid võimaldavad teil luua uusi string-literaalide tüüpe, ühendades olemasolevaid string-literaalide tüüpe, number-literaalide tüüpe ja muud. See võimaldab võimsat stringipõhist tüübimanipulatsiooni.

Näide:


type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIEndpoint = `/api/users` | `/api/products`;

type RequestURL = `${HTTPMethod} ${APIEndpoint}`;

// RequestURL on nüüd "GET /api/users" | "POST /api/users" | "PUT /api/users" | "DELETE /api/users" | "GET /api/products" | "POST /api/products" | "PUT /api/products" | "DELETE /api/products"

function makeRequest(url: RequestURL): void {
  console.log(`Päringu tegemine aadressile ${url}`);
}

makeRequest("GET /api/users"); // Kehtiv
// makeRequest("INVALID /api/users"); // Viga

Tingimuslikud tüübid

Tingimuslikud tüübid võimaldavad teil määratleda tüüpe, mis sõltuvad tüübisuhtena väljendatud tingimusest. Nad kasutavad infer võtmesõna tüübiinfo eraldamiseks.

Näide:


type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

// Kui T on Promise, siis on tüüp U; vastasel juhul on tüüp T.

async function fetchData(): Promise<number> {
  return 42;
}


type Data = UnwrapPromise<ReturnType<typeof fetchData>>;

// Data on nüüd number

function processData(data: Data): void {
  console.log(data * 2);
}

processData(await fetchData());

Praktilised rakendused ja reaalsed stsenaariumid

Uurime keerukamaid reaalseid stsenaariume, kus utiliittüübid säravad.

1. Vormide käsitlemine

Vormidega tegelemisel on teil sageli stsenaariume, kus peate esindama vormi algväärtusi, uuendatud vormi väärtusi ja lõplikke esitatud väärtusi. Utiliittüübid aitavad teil neid erinevaid olekuid tõhusalt hallata.


interface FormData {
  firstName: string;
  lastName: string;
  email: string;
  country: string; // Nõutud
  city?: string; // Valikuline
  postalCode?: string;
  newsletterSubscription?: boolean;
}

// Vormi algväärtused (valikulised väljad)
type InitialFormValues = Partial<FormData>;

// Uuendatud vormi väärtused (mõned väljad võivad puududa)
type UpdatedFormValues = Partial<FormData>;

// Esitamiseks nõutavad väljad
type RequiredForSubmission = Required<Pick<FormData, 'firstName' | 'lastName' | 'email' | 'country'>>;

// Kasutage neid tüüpe oma vormikomponentides
function initializeForm(initialValues: InitialFormValues): void { }
function updateForm(updates: UpdatedFormValues): void {}
function submitForm(data: RequiredForSubmission): void {}


const initialForm: InitialFormValues = { newsletterSubscription: true };

const updateFormValues: UpdatedFormValues = {
    firstName: "John",
    lastName: "Doe"
};

// const submissionData: RequiredForSubmission = { firstName: "test", lastName: "test", email: "test" }; // VIGA: Puudub 'country' 
const submissionData: RequiredForSubmission = { firstName: "test", lastName: "test", email: "test", country: "USA" }; //OK


2. API andmete teisendamine

API-st andmete tarbimisel peate võib-olla andmed oma rakenduse jaoks teise vormingusse teisendama. Utiliittüübid aitavad teil määratleda teisendatud andmete struktuuri.


interface APIResponse {
  user_id: string;
  first_name: string;
  last_name: string;
  email_address: string;
  profile_picture_url: string;
  is_active: boolean;
}

// Teisendage API vastus loetavamasse vormingusse
type UserData = {
  id: string;
  fullName: string;
  email: string;
  avatar: string;
  active: boolean;
};

function transformApiResponse(response: APIResponse): UserData {
  return {
    id: response.user_id,
    fullName: `${response.first_name} ${response.last_name}`,
    email: response.email_address,
    avatar: response.profile_picture_url,
    active: response.is_active
  };
}

function fetchAndTransformData(url: string): Promise<UserData> {
    return fetch(url)
        .then(response => response.json())
        .then(data => transformApiResponse(data));
}


// Saate isegi tüübi jõustada, tehes järgmist:

function saferTransformApiResponse(response: APIResponse): UserData {
    const {user_id, first_name, last_name, email_address, profile_picture_url, is_active} = response;
    const transformed: UserData = {
        id: user_id,
        fullName: `${first_name} ${last_name}`,
        email: email_address,
        avatar: profile_picture_url,
        active: is_active
    };

    return transformed;
}

3. Konfiguratsiooniobjektide käsitlemine

Konfiguratsiooniobjektid on paljudes rakendustes tavalised. Utiliittüübid aitavad teil määratleda konfiguratsiooniobjekti struktuuri ja tagada, et seda kasutatakse õigesti.


interface AppSettings {
  theme: "light" | "dark";
  language: string;
  notificationsEnabled: boolean;
  apiUrl?: string; // Valikuline API URL erinevate keskkondade jaoks
  timeout?: number;  //Valikuline
}

// Vaikeseaded
const defaultSettings: AppSettings = {
  theme: "light",
  language: "en",
  notificationsEnabled: true
};

// Funktsioon kasutaja seadete ühendamiseks vaikeseadetega
function mergeSettings(userSettings: Partial<AppSettings>): AppSettings {
  return { ...defaultSettings, ...userSettings };
}

// Kasutage ühendatud seadeid oma rakenduses
const mergedSettings = mergeSettings({ theme: "dark", apiUrl: "https://customapi.example.com" });
console.log(mergedSettings);

Nõuanded utiliittüüpide tõhusaks kasutamiseks

  • Alustage lihtsalt: Alustage põhiliste utiliittüüpidega nagu Partial ja Readonly, enne kui liigute keerukamate juurde.
  • Kasutage kirjeldavaid nimesid: Andke oma tüübialiasele tähendusrikkaid nimesid loetavuse parandamiseks.
  • Kombineerige utiliittüüpe: Keerukate tüübimuutuste saavutamiseks saate kombineerida mitut utiliittüüpi.
  • Kasutage redaktori tuge: Kasutage ära TypeScripti suurepärast redaktori tuge, et uurida utiliittüüpide mõju.
  • Mõistke aluspõhimõtteid: TypeScripti tüübisüsteemi põhjalik mõistmine on utiliittüüpide tõhusaks kasutamiseks hädavajalik.

Kokkuvõte

TypeScript'i utiliittüübid on võimsad tööriistad, mis võivad oluliselt parandada teie koodi kvaliteeti ja hooldatavust. Mõistes ja rakendades neid utiliittüüpe tõhusalt, saate kirjutada puhtamaid, tüübikindlamaid ja robustsemaid rakendusi, mis vastavad globaalse arendusmaastiku nõudmistele. See juhend on andnud põhjaliku ülevaate levinud utiliittüüpidest ja praktilistest näidetest. Katsetage nendega ja uurige nende potentsiaali oma TypeScripti projektide täiustamiseks. Pidage meeles, et utiliittüüpide kasutamisel tuleb eelistada loetavust ja selgust ning püüda alati kirjutada koodi, mis on kergesti mõistetav ja hooldatav, olenemata sellest, kus teie kaastöötajad asuvad.