A mapped type creates a new type by iterating over the keys of an existing type and transforming each one. It is the mechanism behind TypeScript's most commonly used utility types.
Mapped types cho phép bạn tạo ra một kiểu mới bằng cách lặp qua tất cả các key của một kiểu hiện có và biến đổi chúng. Đây là cơ chế đằng sau nhiều utility types phổ biến nhất của TypeScript như Readonly, Partial, và Required.
1// Basic form: iterate over all keys of T and keep their types2type Copy<T> = {3 [K in keyof T]: T[K];4};56type User = { name: string; age: number; active: boolean };7type UserCopy = Copy<User>;8// { name: string; age: number; active: boolean }910// Transform the value type for every key11type Stringify<T> = {12 [K in keyof T]: string;The [K in keyof T] syntax iterates over every key of T. Within the mapping, K refers to the current key and T[K] is the property's type in the original type.
Cú pháp [K in keyof T] lấy tất cả các key của T và lặp qua chúng. Bạn có thể dùng K để tham chiếu đến key hiện tại và T[K] để lấy kiểu của thuộc tính đó trong T.
Mapped types can add or remove the readonly and optional (?) modifiers from each property. Prefix + adds a modifier (the default), prefix - removes it.
Mapped types có thể thêm hoặc xóa các modifier readonly và optional (?) khỏi mỗi thuộc tính. Tiền tố + thêm modifier (mặc định), tiền tố - xóa modifier.
1type User = { name: string; age?: number; readonly id: string };23// Add readonly to every property (same as built-in Readonly<T>)4type Readonly<T> = {5 readonly [K in keyof T]: T[K];6};78// Add ? to every property (same as built-in Partial<T>)9type Partial<T> = {10 [K in keyof T]?: T[K];11};12- prefix is the only way to strip readonly or ? from a property. Without it, adding the modifier when one already exists is a no-op rather than an error.Modifier - (trừ) là cách duy nhất để xóa readonly hoặc optional khỏi thuộc tính. Đây là tính năng rất hữu ích khi bạn muốn tạo phiên bản 'mutable' hoặc 'complete' của một kiểu.
TypeScript 4.1 introduced key remapping via an as clause. This lets you transform key names — not just value types — inside a mapped type.
TypeScript 4.1 giới thiệu khả năng đổi tên các key trong mapped type bằng mệnh đề as. Điều này cho phép bạn biến đổi tên key, không chỉ kiểu của giá trị.
1// Prefix every key with "get" and capitalize it2type Getters<T> = {3 [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];4};56type User = { name: string; age: number };7type UserGetters = Getters<User>;8// { getName: () => string; getAge: () => number }910// Suffix every key with "Input" for form state11type FormInputs<T> = {12 [K in keyof T as `${string & K}Input`]: string;Returning never from the as clause removes that key from the resulting type. This is the idiomatic way to conditionally exclude properties inside a mapped type.
Trả về never từ mệnh đề as sẽ loại bỏ key đó khỏi kiểu kết quả. Đây là cách chuẩn để lọc các thuộc tính theo điều kiện trong mapped type.
1// Keep only string-valued properties2type StringProps<T> = {3 [K in keyof T as T[K] extends string ? K : never]: T[K];4};56type Mixed = { name: string; age: number; bio: string; active: boolean };78type OnlyStrings = StringProps<Mixed>;9// { name: string; bio: string }1011// Keep only method (function) properties12type Methods<T> = {Mapped types and conditional types compose naturally. Use a conditional type on the value side to transform each property's type based on its own shape.
Mapped types và conditional types là cặp đôi hoàn hảo. Bạn có thể dùng conditional type ở phía giá trị để biến đổi kiểu của từng thuộc tính dựa trên kiểu của chính thuộc tính đó.
1// Wrap every property in a Promise2type Promisify<T> = {3 [K in keyof T]: Promise<T[K]>;4};56// Unwrap Promise from every property7type Awaited<T> = {8 [K in keyof T]: T[K] extends Promise<infer R> ? R : T[K];9};1011// Make nullable properties optional12type NullableToOptional<T> = {Here are production-grade mapped type patterns found in large TypeScript codebases — from API normalisation to form schema generation.
Dưới đây là các pattern mapped type thực tế thường gặp trong các codebase TypeScript lớn — từ API response normalization đến form validation schemas.
1// Record<K, V> — maps a set of keys to a value type2type Record<K extends string | number | symbol, V> = {3 [P in K]: V;4};5type UserIndex = Record<string, { name: string }>;67// Pick<T, K> — keep only the specified keys8type Pick<T, K extends keyof T> = {9 [P in K]: T[P];10};11type UserPreview = Pick<User, "name" | "id">;12TypeScript supports recursive mapped types, letting you apply transformations to every level of a nested object type. Always guard with a conditional type to stop recursion at non-object leaves.
TypeScript hỗ trợ mapped types đệ quy, cho phép bạn áp dụng biến đổi xuống đến mọi cấp độ lồng nhau của một object type. Hãy dùng conditional type để kiểm tra xem thuộc tính có phải là object trước khi đệ quy.
1// Deep readonly — every nested object is also frozen2type DeepReadonly<T> = {3 readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];4};56// Deep required — remove optional from every level7type DeepRequired<T> = {8 [K in keyof T]-?: T[K] extends object ? DeepRequired<T[K]> : T[K];9};1011// Deep record — every leaf value becomes a given type12type DeepRecord<T, V> = {Mapped types đệ quy trên các kiểu vòng tròn (circular types) sẽ gây ra lỗi vô hạn. TypeScript phát hiện một số trường hợp vòng tròn nhưng không phải tất cả — hãy kiểm tra kỹ khi làm việc với các cấu trúc như linked list hay tree nodes.
[K in keyof T]: T[K] là cú pháp cơ bản — lặp qua mọi key và giữ nguyên kiểu của nó
+ adds and - removes readonly and ? modifiers per property+ thêm và - xóa các modifier readonly và ? trên mỗi thuộc tính
as clause remaps key names, commonly with template literal typesMệnh đề as cho phép đổi tên key, thường kết hợp với template literal types
never in the as clause to filter out unwanted keysTrả về never trong as để lọc bỏ key không mong muốn
Readonly, Partial, Required, Pick, Omit, Record are all mapped typesReadonly, Partial, Required, Pick, Omit, Record đều được xây dựng bằng mapped types
Kết hợp với conditional types ở phía giá trị để biến đổi kiểu mỗi thuộc tính theo điều kiện
Mapped types đệ quy áp dụng biến đổi xuống mọi cấp độ lồng nhau của object