Tipuri mapate în TypeScript
Un tip mapat construiește un tip nou parcurgând fiecare proprietate a unui tip existent și schimbând-o în același fel.
- Tipurile mapate = transformă fiecare proprietate a unui tip
- Cele predefinite uzuale:
Partial,Readonly,Pick,Omit,Record
Un mic exemplu de tip mapat
Pornind de la un tip Person, un tip mapat face fiecare proprietate opțională, iar altul face fiecare proprietate readonly.
// Small example
type Person = { name: string; age: number };
type PartialPerson = { [P in keyof Person]?: Person[P] };
type ReadonlyPerson = { readonly [P in keyof Person]: Person[P] };
Sintaxa de bază a tipurilor mapate
Sintaxa de bază a unui tip mapat este { [P in K]: T }, unde:
Peste numele proprietății vizitate în acel momentKeste o uniune a numelor de proprietăți care trebuie parcurse (adeseakeyof SomeType)Teste tipul pe care îl primește fiecare proprietate în tipul nou
Exemplu
PartialPerson adaugă ? la fiecare proprietate a lui Person, așa că este permis un obiect care are doar name. ReadonlyPerson adaugă readonly la fiecare proprietate, așa că o atribuire ulterioară către age ar fi o eroare de compilare.
{ [P in K]: T }, transformând manual o interfață.
Declară o interfață Person cu name: string, age: number și email: string. Construiește un tip mapat PartialPerson care face toate proprietățile opționale și creează un obiect partialPerson doar cu name: "John". Apoi construiește un tip mapat ReadonlyPerson care marchează fiecare proprietate readonly, creează un obiect complet readonlyPerson (Alice, 30, [email protected]) și lasă comentată o reatribuire a lui age, explicând că ar fi o eroare de proprietate doar pentru citire. Nu se afișează nimic.
Ieșire: program TypeScript și rezultatul capturat
// Definim un tip obiect
interface Person {
name: string;
age: number;
email: string;
}
// Creăm un tip mapat care face toate proprietățile opționale
type PartialPerson = {
[P in keyof Person]?: Person[P];
};
// Utilizare
const partialPerson: PartialPerson = {
name: "John"
// age și email sunt opționale
};
// Creăm un tip mapat care face toate proprietățile readonly
type ReadonlyPerson = {
readonly [P in keyof Person]: Person[P];
};
// Utilizare
const readonlyPerson: ReadonlyPerson = {
name: "Alice",
age: 30,
email: "[email protected]"
};
// readonlyPerson.age = 31; // Eroare: nu se poate atribui lui 'age' pentru că este o proprietate doar pentru citire
Tipuri mapate predefinite
Biblioteca standard TypeScript include deja mai multe tipuri mapate utile, așa că nu trebuie să le scrii singur pe cele uzuale:
Partial<T>: face toate proprietățile opționaleReadonly<T>: face toate proprietățile readonlyPick<T, K>: păstrează doar un set ales de cheiOmit<T, K>: elimină un set de cheiRecord<K, V>: asociază unui set de chei un singur tip de valoare
Exemplu
Fiecare alias de mai jos aplică un tip mapat predefinit lui User. Comentariul de după fiecare alias arată tipul obiect pe care îl produce. Este arătat și Required — este opusul lui Partial.
User cu id: number, name: string, email: string și isAdmin: boolean, apoi creează alias-uri de tip folosind Partial<User>, Required<Partial<User>>, Readonly<User>, Pick<User, "email" | "id">, Omit<User, "id" | "isAdmin"> și Record<"admin" | "user" | "guest", string>, fiecare urmat de un comentariu care scrie explicit tipul obiect echivalent. Fișierul doar declară tipuri și nu afișează nimic.
Ieșire: program TypeScript și rezultatul capturat
interface User {
id: number;
name: string;
email: string;
isAdmin: boolean;
}
// Partial<T> - Face toate proprietățile opționale
type PartialUser = Partial<User>;
// Echivalent cu: { id?: number; name?: string; email?: string; isAdmin?: boolean; }
// Required<T> - Face toate proprietățile obligatorii
type RequiredUser = Required<Partial<User>>;
// Echivalent cu: { id: number; name: string; email: string; isAdmin: boolean; }
// Readonly<T> - Face toate proprietățile readonly
type ReadonlyUser = Readonly<User>;
// Echivalent cu: { readonly id: number; readonly name: string; ... }
// Pick<T, K> - Creează un tip cu o submulțime de proprietăți din T
type UserCredentials = Pick<User, "email" | "id">;
// Echivalent cu: { email: string; id: number; }
// Omit<T, K> - Creează un tip eliminând proprietățile indicate din T
type PublicUser = Omit<User, "id" | "isAdmin">;
// Echivalent cu: { name: string; email: string; }
// Record<K, T> - Creează un tip cu cheile și tipurile de valori indicate
type UserRoles = Record<"admin" | "user" | "guest", string>;
// Echivalent cu: { admin: string; user: string; guest: string; }
Crearea de tipuri mapate proprii
Poți scrie propriile tipuri mapate generice, ca să transformi tipurile exact așa cum ai nevoie.
Exemplu
StringifyProperties<T> transformă fiecare proprietate într-un string. Validator<T> transformă fiecare proprietate într-o funcție care primește o valoare de tipul acelei proprietăți (T[P]) și returnează un boolean, așa că productValidator trebuie să ofere câte o verificare pentru fiecare proprietate a lui Product.
Product (id: number, name: string, price: number, inStock: boolean).
Scrie StringifyProperties<T>, care transformă fiecare proprietate în string, și Validator<T>, care transformă fiecare proprietate P într-o funcție (value: T[P]) => boolean. Aplică-l pe primul pentru a obține un tip StringProduct și folosește-l pe al doilea pentru a construi un obiect productValidator ale cărui verificări sunt: id mai mare decât 0, name nevid, price cel puțin 0 și inStock să fie de tip boolean. Nu se afișează nimic.
Ieșire: program TypeScript și rezultatul capturat
// Interfața de bază
interface Product {
id: number;
name: string;
price: number;
inStock: boolean;
}
// Creăm un tip mapat care transformă toate proprietățile în tipul string
type StringifyProperties<T> = {
[P in keyof T]: string;
};
// Utilizare
type StringProduct = StringifyProperties<Product>;
// Echivalent cu: { id: string; name: string; price: string; inStock: string; }
// Creăm un tip mapat care adaugă funcții de validare pentru fiecare proprietate
type Validator<T> = {
[P in keyof T]: (value: T[P]) => boolean;
};
// Utilizare
const productValidator: Validator<Product> = {
id: (id) => id > 0,
name: (name) => name.length > 0,
price: (price) => price >= 0,
inStock: (inStock) => typeof inStock === "boolean"
};
Modificarea modificatorilor de proprietate
Un tip mapat poate și să adauge sau să elimine modificatorii de proprietate readonly și ? (opțional). Scriind +readonly sau readonly adaugi modificatorul; scriind -readonly îl elimini. La fel funcționează și pentru ? cu -?.
Exemplu
Mutable<T> folosește -readonly pentru a elimina marcajul de doar citire de la fiecare proprietate a lui Configuration. RequiredProps<T> folosește -? pentru a face obligatorie fiecare proprietate opțională.
readonly și ? folosind -readonly și -?.
Declară o interfață Configuration cu readonly apiKey: string, readonly apiUrl: string, timeout?: number și retries?: number. Scrie Mutable<T> (elimină readonly de la fiecare proprietate) și RequiredProps<T> (elimină ? de la fiecare proprietate), aplică-le pe fiecare pe Configuration ca MutableConfig și RequiredConfig și comentează după fiecare tipul obiect echivalent. Fișierul doar declară tipuri și nu afișează nimic.
Ieșire: program TypeScript și rezultatul capturat
// Interfața de bază cu câteva proprietăți readonly și opționale
interface Configuration {
readonly apiKey: string;
readonly apiUrl: string;
timeout?: number;
retries?: number;
}
// Eliminăm modificatorul readonly de la toate proprietățile
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
// Utilizare
type MutableConfig = Mutable<Configuration>;
// Echivalent cu: { apiKey: string; apiUrl: string; timeout?: number; retries?: number; }
// Facem obligatorii toate proprietățile opționale
type RequiredProps<T> = {
[P in keyof T]-?: T[P];
};
// Utilizare
type RequiredConfig = RequiredProps<Configuration>;
// Echivalent cu: { readonly apiKey: string; readonly apiUrl: string; timeout: number; retries: number; }
Tipuri mapate avansate
Tipurile mapate devin și mai puternice când le combini cu tipuri condiționale. Tipul nou al fiecărei proprietăți poate atunci să depindă de cel vechi.
Exemplu
FormattedResponse<T> verifică fiecare proprietate cu T[P] extends number și convertește proprietățile numerice în string. StringPropsOnly<T> folosește o clauză as pentru a redenumi cheile: proprietățile care nu sunt șiruri sunt mapate la never, ceea ce le elimină din rezultat.
ApiResponse (data: unknown, status: number, message: string, timestamp: number).
Scrie FormattedResponse<T>, care transformă fiecare proprietate de tip number în string și le lasă neschimbate pe celelalte, și StringPropsOnly<T>, care folosește o clauză as de redenumire a cheilor pentru a păstra doar proprietățile al căror tip este string. Aplică-le pe amândouă pe ApiResponse și comentează tipurile rezultate. Nu se afișează nimic.
Ieșire: program TypeScript și rezultatul capturat
// Interfața de bază
interface ApiResponse {
data: unknown;
status: number;
message: string;
timestamp: number;
}
// Tip mapat condițional: convertim fiecare proprietate numerică într-un șir formatat
type FormattedResponse<T> = {
[P in keyof T]: T[P] extends number ? string : T[P];
};
// Utilizare
type FormattedApiResponse = FormattedResponse<ApiResponse>;
// Echivalent cu: { data: unknown; status: string; message: string; timestamp: string; }
// Alt exemplu: filtrăm doar proprietățile de tip string
type StringPropsOnly<T> = {
[P in keyof T as T[P] extends string ? P : never]: T[P];
};
// Utilizare
type ApiResponseStringProps = StringPropsOnly<ApiResponse>;
// Echivalent cu: { message: string; }
Idei principale
Tipurile mapate îți permit să transformi fiecare proprietate a unui tip într-un mod consecvent.
Concepte cheie:
- Transformarea tipurilor: schimbă în bloc tipurile proprietăților
- Modificatori de proprietate: adaugă sau elimină
readonlyși? - Remaparea cheilor: redenumește sau filtrează proprietățile cu o clauză
as - Compunere: combină tipurile mapate cu alte facilități TypeScript, cum ar fi tipurile condiționale
Cazuri de utilizare frecvente:
- Crearea de versiuni doar pentru citire ale tipurilor
- Transformarea tuturor proprietăților în opționale sau obligatorii
- Transformarea tipurilor proprietăților (de exemplu în nullable sau readonly)
- Filtrarea proprietăților după tipul lor
- Crearea de funcții utilitare sigure din punctul de vedere al tipurilor