Mapped Types
Với index type, bạn có thể tự do thiết lập bất kỳ key nào khi gán giá trị, nhưng khi truy cập phải kiểm tra undefined mỗi lần. Nếu format input đã được xác định rõ ràng, bạn có thể cân nhắc sử dụng Mapped Types.
Mapped Types chủ yếu được sử dụng kết hợp với union type. Dưới đây là định nghĩa các ngôn ngữ được hỗ trợ:
tstypeSystemSupportLanguage = "en" | "fr" | "it" | "es";
tstypeSystemSupportLanguage = "en" | "fr" | "it" | "es";
Bạn có thể sử dụng nó như ràng buộc cho key tương tự như index type:
tstypeButterfly = {[key inSystemSupportLanguage ]: string;};
tstypeButterfly = {[key inSystemSupportLanguage ]: string;};
Khi định nghĩa Butterfly như thế này, ngôn ngữ không được hệ thống hỗ trợ (ở đây là de) sẽ không thể được thiết lập và sử dụng:
tsconstbutterflies :Butterfly = {en : "Butterfly",fr : "Papillon",it : "Farfalla",es : "Mariposa",Object literal may only specify known properties, and 'de' does not exist in type 'Butterfly'.2353Object literal may only specify known properties, and 'de' does not exist in type 'Butterfly'.: "Schmetterling", de };
tsconstbutterflies :Butterfly = {en : "Butterfly",fr : "Papillon",it : "Farfalla",es : "Mariposa",Object literal may only specify known properties, and 'de' does not exist in type 'Butterfly'.2353Object literal may only specify known properties, and 'de' does not exist in type 'Butterfly'.: "Schmetterling", de };
Giới thiệu utility types sử dụng Mapped Types và cách thực hiện
Có một utility type tên là Readonly<T> áp dụng readonly cho tất cả các property của object, biến chúng thành read-only.
📄️ Readonly<T>
Biến tất cả property thành read-only
Readonly<T> cũng được thực hiện bằng tính năng này. Readonly<T> được implement như sau:
tstypeReadonly <T > = {readonly [P in keyofT ]:T [P ];};
tstypeReadonly <T > = {readonly [P in keyofT ]:T [P ];};
Biểu thức keyof T có vẻ xa lạ, nhưng bạn có thể hiểu nó là chuyển đổi các key của object thành union type. Chi tiết về keyof xem tại type operator.
📄️ keyof type operator
keyof là type operator trả về tên các property của một object type dưới dạng kiểu. Ví dụ, khi sử dụng keyof với một kiểu có property name, bạn sẽ nhận được string literal type "name".
mapping modifier
Bằng cách thêm - vào đầu và viết -readonly, bạn có thể tạo ra Mutable<T> để biến các property read-only thành có thể thay đổi (đây không phải là utility type có sẵn). Dấu - này được gọi là mapping modifier.
tstypeImmutableButterfly =Readonly <Butterfly >;typeMutableButterfly = {-readonly [key inSystemSupportLanguage ]: string;};constimmutableButterfly :ImmutableButterfly = {en : "Butterfly",fr : "Papillon",it : "Farfalla",es : "Mariposa",};Cannot assign to 'en' because it is a read-only property.2540Cannot assign to 'en' because it is a read-only property.immutableButterfly .= "Schmetterling"; en constmutableButterfly :MutableButterfly = {en : "Butterfly",fr : "Papillon",it : "Farfalla",es : "Mariposa",};mutableButterfly .en = "Schmetterling"; // OK
tstypeImmutableButterfly =Readonly <Butterfly >;typeMutableButterfly = {-readonly [key inSystemSupportLanguage ]: string;};constimmutableButterfly :ImmutableButterfly = {en : "Butterfly",fr : "Papillon",it : "Farfalla",es : "Mariposa",};Cannot assign to 'en' because it is a read-only property.2540Cannot assign to 'en' because it is a read-only property.immutableButterfly .= "Schmetterling"; en constmutableButterfly :MutableButterfly = {en : "Butterfly",fr : "Papillon",it : "Farfalla",es : "Mariposa",};mutableButterfly .en = "Schmetterling"; // OK
Mapping modifier (-) cũng có thể được thêm vào trước optional modifier và viết thành -? để loại bỏ optional modifier. Sử dụng điều này, bạn có thể implement Required<T> có tác dụng ngược lại với Partial<T>.
📄️ Partial<T>
Biến tất cả property thành optional
📄️ Required<T>
Biến tất cả property thành bắt buộc
Lưu ý về index access
Khi chỉ định kiểu không phải literal type như string cho key trong { [K in string]: ... }, cần chú ý về index access. Bởi vì ngay cả khi truy cập key không tồn tại, nó vẫn được xử lý như thể key đó luôn tồn tại.
Trong ví dụ sau, object dict có kiểu { [K in string]: number } có key a nhưng không có key b. Tuy nhiên, dict.b vẫn được suy luận là number.
tsconstdict : { [K in string]: number } = {a : 1 };dict .b ;
tsconstdict : { [K in string]: number } = {a : 1 };dict .b ;
Giá trị thực tế của dict.b là undefined, nên nếu gọi method của dict.b sẽ gây lỗi runtime.
tsconstdict : { [K in string]: number } = {a : 1 };console .log (dict .b );dict .b .toFixed (); // Xảy ra lỗi runtime
tsconstdict : { [K in string]: number } = {a : 1 };console .log (dict .b );dict .b .toFixed (); // Xảy ra lỗi runtime
Hành vi này không thuận lợi cho các developer muốn giảm runtime error thông qua type checking.
Để giải quyết vấn đề này, TypeScript cung cấp compiler option noUncheckedIndexedAccess. Khi bật option này, kết quả của index access sẽ có kiểu T | undefined. Tức là kiểu này sẽ xem xét khả năng là undefined. Do đó, code gọi method của dict.b sẽ gây compile error và bạn có thể hưởng lợi từ type checking.
tsconstdict : { [K in string]: number } = {a : 1 };dict .b ;'dict.b' is possibly 'undefined'.18048'dict.b' is possibly 'undefined'.dict .b .toFixed ();
tsconstdict : { [K in string]: number } = {a : 1 };dict .b ;'dict.b' is possibly 'undefined'.18048'dict.b' is possibly 'undefined'.dict .b .toFixed ();
📄️ noUncheckedIndexedAccess
Bắt buộc check undefined khi tham chiếu property của index type hoặc phần tử array
Mapped Types không thể thêm property bổ sung
Mapped Types không thể định nghĩa property bổ sung. Đây là điểm khác biệt so với index type.
tstypeKeyValuesAndName = {[K in string]: string;A mapped type may not declare properties or methods.7061A mapped type may not declare properties or methods.: string; // Property bổ sung name };
tstypeKeyValuesAndName = {[K in string]: string;A mapped type may not declare properties or methods.7061A mapped type may not declare properties or methods.: string; // Property bổ sung name };
Nếu có property bổ sung, bạn cần định nghĩa phần đó như một object type riêng và tạo intersection type với Mapped Types.
tstypeKeyValues = {[K in string]: string;};typeName = {name : string; // Property bổ sung};typeKeyValuesAndName =KeyValues &Name ;
tstypeKeyValues = {[K in string]: string;};typeName = {name : string; // Property bổ sung};typeKeyValuesAndName =KeyValues &Name ;
Ví dụ trên cũng có thể gộp thành một kiểu duy nhất:
tstypeKeyValuesAndName = {[K in string]: string;} & {name : string; // Property bổ sung};
tstypeKeyValuesAndName = {[K in string]: string;} & {name : string; // Property bổ sung};