เรียนรู้วิธีการใช้ Mapped Types ของ TypeScript เพื่อแปลงรูปร่างอ็อบเจกต์แบบไดนามิก สร้างโค้ดที่แข็งแกร่งและบำรุงรักษาง่ายสำหรับแอปพลิเคชันระดับโลก
คู่มือฉบับสมบูรณ์: การใช้ Mapped Types ใน TypeScript เพื่อการแปลงอ็อบเจกต์แบบไดนามิก
TypeScript ซึ่งเน้นเรื่อง static typing อย่างมาก ช่วยให้นักพัฒนาสามารถเขียนโค้ดที่น่าเชื่อถือและบำรุงรักษาได้ง่ายขึ้น คุณสมบัติสำคัญที่มีส่วนช่วยในเรื่องนี้อย่างมากคือ mapped types คู่มือนี้จะเจาะลึกเข้าไปในโลกของ mapped types ของ TypeScript เพื่อให้คุณเข้าใจฟังก์ชันการทำงาน ประโยชน์ และการประยุกต์ใช้ในทางปฏิบัติอย่างครอบคลุม โดยเฉพาะอย่างยิ่งในบริบทของการพัฒนาซอฟต์แวร์ระดับโลก
การทำความเข้าใจแนวคิดหลัก
หัวใจสำคัญของ mapped type คือการช่วยให้คุณสร้างไทป์ใหม่ขึ้นจาก property ของไทป์ที่มีอยู่เดิม คุณกำหนดไทป์ใหม่โดยการวนซ้ำผ่านคีย์ของไทป์อื่นและใช้การแปลงกับค่าต่างๆ ซึ่งมีประโยชน์อย่างยิ่งในสถานการณ์ที่คุณต้องการแก้ไขโครงสร้างของอ็อบเจกต์แบบไดนามิก เช่น การเปลี่ยนชนิดข้อมูลของ property, การทำให้ property เป็น optional หรือการเพิ่ม property ใหม่โดยอ้างอิงจาก property ที่มีอยู่
มาเริ่มจากพื้นฐานกันก่อน ลองพิจารณา interface ง่ายๆ นี้:
interface Person {
name: string;
age: number;
email: string;
}
ตอนนี้ เราจะมาสร้าง mapped type ที่ทำให้ property ทั้งหมดของ Person
เป็น optional:
type OptionalPerson = {
[K in keyof Person]?: Person[K];
};
ในตัวอย่างนี้:
[K in keyof Person]
จะวนซ้ำผ่านคีย์แต่ละตัว (name
,age
,email
) ของPerson
interface?
ทำให้ property แต่ละตัวเป็น optionalPerson[K]
อ้างอิงถึงไทป์ของ property ในPerson
interface ต้นฉบับ
ไทป์ OptionalPerson
ที่ได้จะมีลักษณะดังนี้:
{
name?: string;
age?: number;
email?: string;
}
นี่คือตัวอย่างที่แสดงให้เห็นถึงพลังของ mapped types ในการแก้ไขไทป์ที่มีอยู่แบบไดนามิก
ไวยากรณ์และโครงสร้างของ Mapped Types
ไวยากรณ์ของ mapped type นั้นค่อนข้างเฉพาะเจาะจงและมีโครงสร้างทั่วไปดังนี้:
type NewType = {
[Key in KeysType]: ValueType;
};
มาดูส่วนประกอบแต่ละส่วนกัน:
NewType
: ชื่อที่คุณกำหนดให้กับไทป์ใหม่ที่กำลังสร้างขึ้น[Key in KeysType]
: นี่คือหัวใจของ mapped type โดยKey
คือตัวแปรที่วนซ้ำผ่านสมาชิกแต่ละตัวของKeysType
ซึ่งKeysType
มักจะเป็นkeyof
ของไทป์อื่น (เหมือนในตัวอย่างOptionalPerson
ของเรา) แต่ไม่เสมอไป นอกจากนี้ยังสามารถเป็น union ของ string literals หรือไทป์ที่ซับซ้อนกว่านั้นได้ValueType
: ส่วนนี้จะระบุไทป์ของ property ในไทป์ใหม่ ซึ่งอาจเป็นไทป์โดยตรง (เช่นstring
), ไทป์ที่อ้างอิงจาก property ของไทป์ต้นฉบับ (เช่นPerson[K]
), หรือการแปลงที่ซับซ้อนกว่านั้นของไทป์เดิม
ตัวอย่าง: การแปลงประเภทของ Property
สมมติว่าคุณต้องการแปลง property ที่เป็นตัวเลขทั้งหมดของอ็อบเจกต์ให้เป็นสตริง คุณสามารถทำได้โดยใช้ mapped type ดังนี้:
interface Product {
id: number;
name: string;
price: number;
quantity: number;
}
type StringifiedProduct = {
[K in keyof Product]: Product[K] extends number ? string : Product[K];
};
ในกรณีนี้ เรากำลัง:
- วนซ้ำผ่านคีย์แต่ละตัวของ
Product
interface - ใช้ conditional type (
Product[K] extends number ? string : Product[K]
) เพื่อตรวจสอบว่า property เป็นตัวเลขหรือไม่ - ถ้าเป็นตัวเลข เราจะกำหนดไทป์ของ property ให้เป็น
string
; มิฉะนั้น เราจะคงไทป์เดิมไว้
ไทป์ StringifiedProduct
ที่ได้จะเป็น:
{
id: string;
name: string;
price: string;
quantity: string;
}
คุณสมบัติและเทคนิคที่สำคัญ
1. การใช้ keyof
และ Index Signatures
ดังที่แสดงให้เห็นก่อนหน้านี้ keyof
เป็นเครื่องมือพื้นฐานสำหรับการทำงานกับ mapped types ซึ่งช่วยให้คุณสามารถวนซ้ำคีย์ของไทป์ได้ ส่วน Index Signatures เป็นวิธีในการกำหนดไทป์ของ property เมื่อคุณไม่ทราบคีย์ล่วงหน้า แต่ยังต้องการแปลงค่าเหล่านั้น
ตัวอย่าง: การแปลง Property ทั้งหมดโดยใช้ Index Signature
interface StringMap {
[key: string]: number;
}
type StringMapToString = {
[K in keyof StringMap]: string;
};
ในที่นี้ ค่าที่เป็นตัวเลขทั้งหมดใน StringMap จะถูกแปลงเป็นสตริงในไทป์ใหม่
2. Conditional Types ภายใน Mapped Types
Conditional types เป็นคุณสมบัติที่ทรงพลังของ TypeScript ที่ช่วยให้คุณสามารถแสดงความสัมพันธ์ของไทป์ตามเงื่อนไขได้ เมื่อใช้ร่วมกับ mapped types จะช่วยให้สามารถทำการแปลงที่ซับซ้อนสูงได้
ตัวอย่าง: การลบ Null และ Undefined ออกจากไทป์
type NonNullableProperties = {
[K in keyof T]: T[K] extends (null | undefined) ? never : T[K];
};
mapped type นี้จะวนซ้ำคีย์ทั้งหมดของไทป์ T
และใช้ conditional type เพื่อตรวจสอบว่าค่านั้นอนุญาตให้เป็น null หรือ undefined หรือไม่ ถ้าใช่ ไทป์จะถูกประเมินเป็น never ซึ่งเป็นการลบ property นั้นออกไปอย่างมีประสิทธิภาพ มิฉะนั้นจะคงไทป์เดิมไว้ วิธีนี้ทำให้ไทป์มีความแข็งแกร่งมากขึ้นโดยการกำจัดค่า null หรือ undefined ที่อาจก่อปัญหา ซึ่งช่วยปรับปรุงคุณภาพของโค้ดและสอดคล้องกับแนวปฏิบัติที่ดีที่สุดสำหรับการพัฒนาซอฟต์แวร์ระดับโลก
3. Utility Types เพื่อประสิทธิภาพ
TypeScript มี utility types ในตัวที่ช่วยลดความซับซ้อนของงานจัดการไทป์ทั่วไป ซึ่งไทป์เหล่านี้ใช้ mapped types อยู่เบื้องหลัง
Partial
: ทำให้ property ทั้งหมดของไทป์T
เป็น optional (ดังที่แสดงในตัวอย่างก่อนหน้า)Required
: ทำให้ property ทั้งหมดของไทป์T
เป็น requiredReadonly
: ทำให้ property ทั้งหมดของไทป์T
เป็น read-onlyPick
: สร้างไทป์ใหม่ที่มีเฉพาะคีย์ที่ระบุ (K
) จากไทป์T
Omit
: สร้างไทป์ใหม่ที่มี property ทั้งหมดของไทป์T
ยกเว้นคีย์ที่ระบุ (K
)
ตัวอย่าง: การใช้ Pick
และ Omit
interface User {
id: number;
name: string;
email: string;
role: string;
}
type UserSummary = Pick;
// { id: number; name: string; }
type UserWithoutEmail = Omit;
// { id: number; name: string; role: string; }
utility types เหล่านี้ช่วยให้คุณไม่ต้องเขียน mapped type ที่ซ้ำซ้อนและช่วยเพิ่มความสามารถในการอ่านโค้ด โดยมีประโยชน์อย่างยิ่งในการพัฒนาระดับโลกเพื่อจัดการมุมมองหรือระดับการเข้าถึงข้อมูลที่แตกต่างกันตามสิทธิ์ของผู้ใช้หรือบริบทของแอปพลิเคชัน
การประยุกต์ใช้และตัวอย่างในโลกจริง
1. การตรวจสอบและแปลงข้อมูล
Mapped types มีคุณค่าอย่างยิ่งสำหรับการตรวจสอบและแปลงข้อมูลที่ได้รับจากแหล่งภายนอก (เช่น API, ฐานข้อมูล, ข้อมูลที่ผู้ใช้ป้อน) นี่เป็นสิ่งสำคัญในแอปพลิเคชันระดับโลกที่คุณอาจต้องจัดการกับข้อมูลจากหลายแหล่งที่แตกต่างกันและจำเป็นต้องรับประกันความสมบูรณ์ของข้อมูล โดยช่วยให้คุณสามารถกำหนดกฎเฉพาะ เช่น การตรวจสอบชนิดข้อมูล และแก้ไขโครงสร้างข้อมูลโดยอัตโนมัติตามกฎเหล่านั้น
ตัวอย่าง: การแปลงข้อมูลตอบกลับจาก API
interface ApiResponse {
userId: string;
id: string;
title: string;
completed: boolean;
}
type CleanedApiResponse = {
[K in keyof ApiResponse]:
K extends 'userId' | 'id' ? number :
K extends 'title' ? string :
K extends 'completed' ? boolean : any;
};
ตัวอย่างนี้แปลง property userId
และ id
(ซึ่งเดิมเป็นสตริงจาก API) ให้เป็นตัวเลข ส่วน property title
ถูกกำหนดไทป์เป็นสตริงอย่างถูกต้อง และ completed
ยังคงเป็น boolean ซึ่งช่วยรับประกันความสอดคล้องของข้อมูลและหลีกเลี่ยงข้อผิดพลาดที่อาจเกิดขึ้นในการประมวลผลถัดไป
2. การสร้าง Props สำหรับคอมโพเนนต์ที่นำกลับมาใช้ใหม่ได้
ใน React และ UI framework อื่นๆ mapped types สามารถทำให้การสร้าง props สำหรับคอมโพเนนต์ที่นำกลับมาใช้ใหม่ได้ง่ายขึ้น ซึ่งมีความสำคัญอย่างยิ่งเมื่อพัฒนา UI component ระดับโลกที่ต้องปรับให้เข้ากับภาษาและส่วนติดต่อผู้ใช้ที่แตกต่างกัน
ตัวอย่าง: การจัดการ Localization
interface TextProps {
textId: string;
defaultText: string;
locale: string;
}
type LocalizedTextProps = {
[K in keyof TextProps as `localized-${K}`]: TextProps[K];
};
ในโค้ดนี้ ไทป์ใหม่ LocalizedTextProps
จะเพิ่มคำนำหน้าให้กับชื่อ property แต่ละตัวของ TextProps
ตัวอย่างเช่น textId
จะกลายเป็น localized-textId
ซึ่งมีประโยชน์สำหรับการตั้งค่า props ของคอมโพเนนต์ รูปแบบนี้สามารถใช้เพื่อสร้าง props ที่อนุญาตให้เปลี่ยนข้อความแบบไดนามิกตามภาษาของผู้ใช้ ซึ่งจำเป็นอย่างยิ่งสำหรับการสร้างส่วนติดต่อผู้ใช้หลายภาษาที่ทำงานได้อย่างราบรื่นในภูมิภาคและภาษาต่างๆ เช่น ในแอปพลิเคชันอีคอมเมิร์ซหรือแพลตฟอร์มโซเชียลมีเดียระหว่างประเทศ props ที่ถูกแปลงแล้วจะช่วยให้นักพัฒนาสามารถควบคุม localization ได้มากขึ้นและสามารถสร้างประสบการณ์ผู้ใช้ที่สอดคล้องกันทั่วโลกได้
3. การสร้างฟอร์มแบบไดนามิก
Mapped types มีประโยชน์สำหรับการสร้างฟิลด์ฟอร์มแบบไดนามิกตามโมเดลข้อมูล ในแอปพลิเคชันระดับโลก สิ่งนี้มีประโยชน์สำหรับการสร้างฟอร์มที่ปรับให้เข้ากับบทบาทของผู้ใช้หรือข้อกำหนดข้อมูลที่แตกต่างกัน
ตัวอย่าง: การสร้างฟิลด์ฟอร์มอัตโนมัติตามคีย์ของอ็อบเจกต์
interface UserProfile {
firstName: string;
lastName: string;
email: string;
phoneNumber: string;
}
type FormFields = {
[K in keyof UserProfile]: {
label: string;
type: string;
required: boolean;
};
};
สิ่งนี้ช่วยให้คุณสามารถกำหนดโครงสร้างฟอร์มตาม property ของ UserProfile
interface ซึ่งช่วยหลีกเลี่ยงความจำเป็นในการกำหนดฟิลด์ฟอร์มด้วยตนเอง ทำให้แอปพลิเคชันของคุณมีความยืดหยุ่นและบำรุงรักษาง่ายขึ้น
เทคนิค Mapped Type ขั้นสูง
1. การแมปคีย์ใหม่ (Key Remapping)
TypeScript 4.1 ได้เปิดตัวการแมปคีย์ใหม่ใน mapped types ซึ่งช่วยให้คุณสามารถเปลี่ยนชื่อคีย์ไปพร้อมกับการแปลงไทป์ได้ นี่เป็นประโยชน์อย่างยิ่งเมื่อต้องปรับไทป์ให้เข้ากับข้อกำหนด API ที่แตกต่างกัน หรือเมื่อคุณต้องการสร้างชื่อ property ที่เป็นมิตรต่อผู้ใช้มากขึ้น
ตัวอย่าง: การเปลี่ยนชื่อ Property
interface Product {
productId: number;
productName: string;
productDescription: string;
price: number;
}
type ProductDto = {
[K in keyof Product as `dto_${K}`]: Product[K];
};
โค้ดนี้จะเปลี่ยนชื่อ property แต่ละตัวของไทป์ Product
ให้ขึ้นต้นด้วย dto_
ซึ่งมีประโยชน์อย่างยิ่งเมื่อทำการแมประหว่างโมเดลข้อมูลและ API ที่ใช้รูปแบบการตั้งชื่อที่แตกต่างกัน และมีความสำคัญในการพัฒนาซอฟต์แวร์ระหว่างประเทศที่แอปพลิเคชันต้องเชื่อมต่อกับระบบหลังบ้านหลายระบบซึ่งอาจมีรูปแบบการตั้งชื่อเฉพาะ ทำให้การรวมระบบเป็นไปอย่างราบรื่น
2. การแมปคีย์ใหม่แบบมีเงื่อนไข
คุณสามารถรวมการแมปคีย์ใหม่เข้ากับ conditional types เพื่อการแปลงที่ซับซ้อนยิ่งขึ้น ทำให้คุณสามารถเปลี่ยนชื่อหรือยกเว้น property ตามเกณฑ์บางอย่างได้ เทคนิคนี้ช่วยให้สามารถทำการแปลงที่ซับซ้อนได้
ตัวอย่าง: การยกเว้น Property ออกจาก DTO
interface Product {
id: number;
name: string;
description: string;
price: number;
category: string;
isActive: boolean;
}
type ProductDto = {
[K in keyof Product as K extends 'description' | 'isActive' ? never : K]: Product[K]
}
ในที่นี้ property description
และ isActive
จะถูกลบออกจากไทป์ ProductDto
ที่สร้างขึ้นอย่างมีประสิทธิภาพ เนื่องจากคีย์จะถูกประเมินเป็น never
หาก property นั้นคือ 'description' หรือ 'isActive' ซึ่งช่วยให้สามารถสร้าง Data Transfer Objects (DTOs) เฉพาะที่มีเฉพาะข้อมูลที่จำเป็นสำหรับการดำเนินการต่างๆ การถ่ายโอนข้อมูลแบบเลือกเช่นนี้มีความสำคัญต่อการเพิ่มประสิทธิภาพและความเป็นส่วนตัวในแอปพลิเคชันระดับโลก ข้อจำกัดในการถ่ายโอนข้อมูลช่วยให้มั่นใจได้ว่ามีเพียงข้อมูลที่เกี่ยวข้องเท่านั้นที่ถูกส่งผ่านเครือข่าย ซึ่งช่วยลดการใช้แบนด์วิดท์และปรับปรุงประสบการณ์ผู้ใช้ให้ดีขึ้น ซึ่งสอดคล้องกับกฎระเบียบด้านความเป็นส่วนตัวทั่วโลก
3. การใช้ Mapped Types ร่วมกับ Generics
Mapped types สามารถใช้ร่วมกับ generics เพื่อสร้างการกำหนดไทป์ที่ยืดหยุ่นและนำกลับมาใช้ใหม่ได้สูง ซึ่งช่วยให้คุณสามารถเขียนโค้ดที่สามารถจัดการกับไทป์ที่หลากหลายได้ ทำให้โค้ดของคุณสามารถนำกลับมาใช้ใหม่และบำรุงรักษาได้ง่ายขึ้นอย่างมาก ซึ่งมีคุณค่าอย่างยิ่งในโครงการขนาดใหญ่และทีมงานระหว่างประเทศ
ตัวอย่าง: ฟังก์ชัน Generic สำหรับการแปลงค่า Property ของอ็อบเจกต์
function transformObjectValues(obj: T, transform: (value: T[K]) => U): {
[P in keyof T]: U;
} {
const result: any = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = transform(obj[key]);
}
}
return result;
}
interface Order {
id: number;
items: string[];
total: number;
}
const order: Order = {
id: 123,
items: ['apple', 'banana'],
total: 5.99,
};
const stringifiedOrder = transformObjectValues(order, (value) => String(value));
// stringifiedOrder: { id: string; items: string; total: string; }
ในตัวอย่างนี้ ฟังก์ชัน transformObjectValues
ใช้ generics (T
, K
, และ U
) เพื่อรับอ็อบเจกต์ (obj
) ที่มีไทป์ T
และฟังก์ชัน transform ที่รับ property เดียวจาก T และคืนค่าที่มีไทป์ U จากนั้นฟังก์ชันจะคืนค่าเป็นอ็อบเจกต์ใหม่ที่มีคีย์เหมือนกับอ็อบเจกต์เดิม แต่มีค่าที่ถูกแปลงเป็นไทป์ U แล้ว
แนวปฏิบัติที่ดีที่สุดและข้อควรพิจารณา
1. ความปลอดภัยของไทป์และการบำรุงรักษาโค้ด
หนึ่งในประโยชน์ที่ใหญ่ที่สุดของ TypeScript และ mapped types คือการเพิ่มความปลอดภัยของไทป์ การกำหนดไทป์ที่ชัดเจนช่วยให้คุณตรวจพบข้อผิดพลาดได้เร็วขึ้นในระหว่างการพัฒนา ซึ่งช่วยลดโอกาสเกิดข้อบกพร่องขณะรันไทม์ ทำให้โค้ดของคุณเข้าใจและรีแฟคเตอร์ได้ง่ายขึ้น โดยเฉพาะในโครงการขนาดใหญ่ นอกจากนี้ การใช้ mapped types ยังช่วยให้มั่นใจว่าโค้ดมีโอกาสเกิดข้อผิดพลาดน้อยลงเมื่อซอฟต์แวร์ขยายขนาดขึ้น เพื่อปรับให้เข้ากับความต้องการของผู้ใช้หลายล้านคนทั่วโลก
2. ความสามารถในการอ่านและสไตล์ของโค้ด
แม้ว่า mapped types จะทรงพลัง แต่สิ่งสำคัญคือต้องเขียนให้ชัดเจนและอ่านง่าย ควรใช้ชื่อตัวแปรที่มีความหมายและใส่ความคิดเห็นในโค้ดเพื่ออธิบายวัตถุประสงค์ของการแปลงที่ซับซ้อน ความชัดเจนของโค้ดช่วยให้นักพัฒนาจากทุกพื้นเพสามารถอ่านและทำความเข้าใจโค้ดได้ ความสอดคล้องในสไตล์การเขียน รูปแบบการตั้งชื่อ และการจัดรูปแบบทำให้โค้ดเข้าถึงได้ง่ายขึ้นและมีส่วนช่วยให้กระบวนการพัฒนาราบรื่นขึ้น โดยเฉพาะในทีมงานระหว่างประเทศที่สมาชิกต่างทำงานในส่วนต่างๆ ของซอฟต์แวร์
3. การใช้งานมากเกินไปและความซับซ้อน
หลีกเลี่ยงการใช้ mapped types มากเกินไป แม้ว่าจะมีประสิทธิภาพ แต่ก็อาจทำให้โค้ดอ่านยากขึ้นหากใช้มากเกินไปหรือเมื่อมีวิธีแก้ปัญหาที่ง่ายกว่า ลองพิจารณาว่าการกำหนด interface แบบตรงไปตรงมาหรือฟังก์ชัน utility ง่ายๆ อาจเป็นทางออกที่เหมาะสมกว่าหรือไม่ หากไทป์ของคุณซับซ้อนเกินไป อาจเป็นเรื่องยากที่จะทำความเข้าใจและบำรุงรักษา ควรพิจารณาความสมดุลระหว่างความปลอดภัยของไทป์และความสามารถในการอ่านโค้ดเสมอ การสร้างสมดุลนี้จะช่วยให้สมาชิกทุกคนในทีมระหว่างประเทศสามารถอ่าน ทำความเข้าใจ และบำรุงรักษาโค้ดเบสได้อย่างมีประสิทธิภาพ
4. ประสิทธิภาพ
Mapped types ส่วนใหญ่มีผลต่อการตรวจสอบไทป์ในขณะคอมไพล์และโดยทั่วไปไม่ได้สร้างภาระด้านประสิทธิภาพขณะรันไทม์อย่างมีนัยสำคัญ อย่างไรก็ตาม การจัดการไทป์ที่ซับซ้อนเกินไปอาจทำให้กระบวนการคอมไพล์ช้าลงได้ ควรลดความซับซ้อนและพิจารณาผลกระทบต่อเวลาในการ build โดยเฉพาะในโครงการขนาดใหญ่หรือสำหรับทีมที่กระจายตัวอยู่ตามเขตเวลาต่างๆ และมีข้อจำกัดด้านทรัพยากรที่แตกต่างกัน
สรุป
TypeScript mapped types เป็นชุดเครื่องมือที่ทรงพลังสำหรับการแปลงรูปร่างของอ็อบเจกต์แบบไดนามิก ซึ่งมีคุณค่าอย่างยิ่งในการสร้างโค้ดที่ปลอดภัยต่อไทป์ บำรุงรักษาง่าย และนำกลับมาใช้ใหม่ได้ โดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับโมเดลข้อมูลที่ซับซ้อน การโต้ตอบกับ API และการพัฒนา UI component การเรียนรู้ mapped types อย่างเชี่ยวชาญจะช่วยให้คุณสามารถเขียนแอปพลิเคชันที่แข็งแกร่งและปรับเปลี่ยนได้มากขึ้น สร้างซอฟต์แวร์ที่ดีกว่าสำหรับตลาดโลก สำหรับทีมงานระหว่างประเทศและโครงการระดับโลก การใช้ mapped types ช่วยให้ได้โค้ดที่มีคุณภาพและบำรุงรักษาง่าย คุณสมบัติที่กล่าวถึงในที่นี้มีความสำคัญอย่างยิ่งต่อการสร้างซอฟต์แวร์ที่ปรับเปลี่ยนได้และขยายขนาดได้ ซึ่งช่วยปรับปรุงความสามารถในการบำรุงรักษาโค้ดและสร้างประสบการณ์ที่ดีขึ้นสำหรับผู้ใช้ทั่วโลก Mapped types ทำให้โค้ดง่ายต่อการอัปเดตเมื่อมีการเพิ่มหรือแก้ไขคุณสมบัติใหม่, API หรือโมเดลข้อมูล