Bạn có quyền được nhầm lẫn. Chữ ký chỉ số có nghĩa là một vài điều, và chúng có nghĩa là những điều hơi khác nhau tùy thuộc vào nơi và cách bạn yêu cầu.
Đầu tiên, chữ ký chỉ mục ngụ ý rằng tất cả các thuộc tính được khai báo trong loại phải có loại tương thích .
interface NotLegal {
// Error, 'string' isn't assignable to 'number'
x: string;
[key: string]: number;
}
Đây là khía cạnh xác định của chữ ký chỉ mục - rằng chúng mô tả một đối tượng với các khóa thuộc tính khác nhau, nhưng là một loại nhất quán trên tất cả các khóa đó. Quy tắc này ngăn không cho một loại không chính xác được quan sát khi một thuộc tính được truy cập thông qua một hướng dẫn:
function fn(obj: NotLegal) {
// 'n' would have a 'string' value
const n: number = obj[String.fromCharCode(120)];
}
Thứ hai, chữ ký chỉ mục cho phép ghi vào bất kỳ chỉ mục nào với loại tương thích .
interface NameMap {
[name: string]: number;
}
function setAge(ageLookup: NameMap, name: string, age: number) {
ageLookup[name] = age;
}
Đây là trường hợp sử dụng khóa cho chữ ký chỉ mục: Bạn có một số bộ khóa và bạn muốn lưu trữ một giá trị được liên kết với khóa.
Thứ ba, chữ ký chỉ số ngụ ý sự tồn tại của bất kỳ tài sản nào bạn yêu cầu cụ thể :
interface NameMap {
[name: string]: number;
}
function getMyAge(ageLookup: NameMap) {
// Inferred return type is 'number'
return ageLookup["RyanC"];
}
Bởi vì x["p"]
và x.p
có hành vi giống hệt nhau trong JavaScript, TypeScript xử lý chúng tương đương:
// Equivalent
function getMyAge(ageLookup: NameMap) {
return ageLookup.RyanC;
}
Điều này phù hợp với cách TypeScript xem các mảng, đó là quyền truy cập mảng được coi là giới hạn. Nó cũng thuận tiện cho chữ ký chỉ mục bởi vì, thông thường, bạn có sẵn một bộ khóa đã biết và không cần thực hiện bất kỳ kiểm tra bổ sung nào:
interface NameMap {
[name: string]: number;
}
function getAges(ageLookup: NameMap) {
const ages = [];
for (const k of Object.keys(ageLookup)) {
ages.push(ageLookup[k]);
}
return ages;
}
Tuy nhiên, chữ ký chỉ mục không ngụ ý rằng bất kỳ thuộc tính tùy ý, không cụ thể nào sẽ xuất hiện khi liên quan đến một loại có chữ ký chỉ mục với một loại có thuộc tính khai báo:
interface Point {
x: number;
y: number;
}
interface NameMap {
[name: string]: number;
}
const m: NameMap = {};
// Not OK, which is good, because p.x is undefined
const p: Point = m;
Loại bài tập này rất khó có thể đúng trong thực tế!
Đây là một tính năng phân biệt giữa { [k: string]: any }
và any
chính nó - bạn có thể đọc và ghi các thuộc tính của bất kỳ loại nào trên một đối tượng có chữ ký chỉ mục, nhưng nó không thể được sử dụng thay cho bất kỳ loại nào any
có thể.
Mỗi hành vi này là cá nhân rất chính đáng, nhưng nói chung, một số mâu thuẫn có thể quan sát được.
Ví dụ, hai hàm này giống hệt nhau về hành vi thời gian chạy của chúng, nhưng TypeScript chỉ coi một trong số chúng được gọi không chính xác:
interface Point {
x: number;
y: number;
}
interface NameMap {
[name: string]: number;
}
function A(x: NameMap) {
console.log(x.y);
}
function B(x: Point) {
console.log(x.y);
}
const m: NameMap = { };
A(m); // OK
B(m); // Error
Nhìn chung, khi bạn viết chữ ký chỉ mục trên một loại, bạn đang nói:
- Bạn có thể đọc / ghi đối tượng này bằng một phím tùy ý
- Khi tôi đọc một thuộc tính cụ thể từ đối tượng này thông qua khóa tùy ý, nó luôn xuất hiện
- Đối tượng này không có nghĩa đen là mỗi tên thuộc tính tồn tại cho mục đích tương thích kiểu