Nhảy tới nội dung

Record<Keys, Type>

Record<Keys, Type> là utility type tạo object type với property key là Keys và property value là Type.

Type argument của Record<Keys, Type>

Keys

Chỉ định property key của object. Kiểu có thể gán cho Keysstring, number, symbol và literal type của chúng.

Type

Chỉ định kiểu của property value của object. Có thể gán bất kỳ kiểu nào.

Ví dụ sử dụng Record

Định nghĩa index type với key là string và value là number:

ts
type StringNumber = Record<string, number>;
const value: StringNumber = { a: 1, b: 2, c: 3 };
ts
type StringNumber = Record<string, number>;
const value: StringNumber = { a: 1, b: 2, c: 3 };

Định nghĩa object type với key là firstName, middleName, familyName và value là string:

ts
type Person = Record<"firstName" | "middleName" | "lastName", string>;
const person: Person = {
firstName: "Robert",
middleName: "Cecil",
lastName: "Martin",
};
ts
type Person = Record<"firstName" | "middleName" | "lastName", string>;
const person: Person = {
firstName: "Robert",
middleName: "Cecil",
lastName: "Martin",
};

Lưu ý về index access

Khi chỉ định kiểu không phải literal type như string cho key như Record<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 Record<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.

ts
const dict: Record<string, number> = { a: 1 };
dict.b;
number
ts
const dict: Record<string, number> = { a: 1 };
dict.b;
number

Giá trị thực tế của dict.bundefined, nên nếu gọi method của dict.b sẽ gây lỗi runtime.

ts
const dict: Record<string, number> = { a: 1 };
console.log(dict.b);
undefined
dict.b.toFixed(); // 実行時エラーが発生する
ts
const dict: Record<string, number> = { a: 1 };
console.log(dict.b);
undefined
dict.b.toFixed(); // 実行時エラーが発生する

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.

ts
const dict: Record<string, number> = { a: 1 };
dict.b;
number | undefined
dict.b.toFixed();
'dict.b' is possibly 'undefined'.18048'dict.b' is possibly 'undefined'.
ts
const dict: Record<string, number> = { a: 1 };
dict.b;
number | undefined
dict.b.toFixed();
'dict.b' is possibly 'undefined'.18048'dict.b' is possibly 'undefined'.

📄️ noUncheckedIndexedAccess

Bắt buộc check undefined khi tham chiếu property của index type hoặc phần tử array

Mặt khác, khi key của Record chỉ bao gồm literal type như "firstName" | "lastName", vấn đề này không xảy ra bất kể cài đặt noUncheckedIndexedAccess. Vì key bị giới hạn, truy cập key không tồn tại sẽ gây compile error.

ts
// noUncheckedIndexedAccessがfalseの場合
type Person = Record<"firstName" | "lastName", string>;
const person: Person = {
firstName: "Robert",
lastName: "Martin",
};
const firstName = person.firstName;
const firstName: string
person.b; // 存在しないキーへのアクセス
Property 'b' does not exist on type 'Person'.2339Property 'b' does not exist on type 'Person'.
ts
// noUncheckedIndexedAccessがfalseの場合
type Person = Record<"firstName" | "lastName", string>;
const person: Person = {
firstName: "Robert",
lastName: "Martin",
};
const firstName = person.firstName;
const firstName: string
person.b; // 存在しないキーへのアクセス
Property 'b' does not exist on type 'Person'.2339Property 'b' does not exist on type 'Person'.

Khi key là string, nếu bật noUncheckedIndexedAccess, compiler sẽ bao gồm undefined, nhưng khi key là literal type (hoặc union của literal type), compiler sẽ không bao gồm undefined. Bởi vì việc key luôn tồn tại đã tự rõ ràng do việc chỉ định key bằng literal type.

ts
// noUncheckedIndexedAccessがtrueの場合
type Person = Record<"firstName" | "lastName", string>;
const person: Person = {
firstName: "Robert",
lastName: "Martin",
};
const firstName = person.firstName; // undefinedは含まれない
const firstName: string
ts
// noUncheckedIndexedAccessがtrueの場合
type Person = Record<"firstName" | "lastName", string>;
const person: Person = {
firstName: "Robert",
lastName: "Martin",
};
const firstName = person.firstName; // undefinedは含まれない
const firstName: string

Thông tin liên quan

📄️ Index signature

Trong TypeScript, có trường hợp muốn chỉ định type của property mà không chỉ định tên field của object. Lúc này có thể sử dụng index signature. Ví dụ, object có tất cả property là type number được type annotation như sau.

📄️ 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.

📄️ Map<K, V>

Map là một trong những built-in API của JavaScript, là object để xử lý cặp key-value. Trong Map, một key chỉ có thể lưu trữ một giá trị duy nhất.