網站搜尋

typescript - 有辦法讓索引簽名的鍵為負數嗎?


我想要一個索引簽名,類型為:[key: number]: string;

例如:

type MoveNbWithLocation = {     
    [key: number]: string; 
}  

然後:

const initPieceLocation: MoveNbWithLocation = {0: ''}; //This is fine
const initPieceLocation: MoveNbWithLocation = {1.3: ''}; //This is also fine
const initPieceLocation: MoveNbWithLocation = {-1: ''}; //This is NOT fine

我偏好使用類型為 'number' 的鍵。

然而,如果無法使用負數,也許我必須將鍵的類型設為 'string'。

答案#1

TL;DR,將數字強制轉換為字串,例如 {"-1": "abc"},或使用計算出的鍵,例如 {[-1]: "abc"}。


JavaScript 物件實際上並沒有數字鍵。屬性名稱永遠只是 string 或 symbol。如果你使用其他類型,JavaScript 會將它強制轉換為 string。當你在物件字面量中寫入數字字面量鍵,例如 {0: ""},這與你先將它轉成字串再寫成 {"0": ""} 是完全一樣的。兩者之間沒有差別。而這兩種寫法也與使用計算屬性寫法 {[0]: ""} 或 {["0"]: ""} 是相同的。

這意味著 TypeScript 的數字索引簽名有點像是一種方便的虛構。它實際上意味著類似「形式為 String(n) 的字符串,其中 n 是某個數字」。數字索引簽名將接受數字鍵,但它也會接受對應於序列化數字的字符串鍵:

let x: MoveNbWithLocation;
x = { 1.3: "abc" }; // okay
x = { "1.3": "abc" }; //okay
x = { 1.30: "abc" }; // okay
x = { "1.30": "abc" }; // error!

前三個都是相同的。數字字面量 1.3 和數字字面量 1.30 是相同的數字,並會被強制轉換為 "1.3"。另一方面,字串 "1.30" 並非你對任何數字執行 String(1.30)、String(1.3) 或 String(n) 後得到的結果,因此它與字串索引簽名不匹配。

這並不明顯,但在 JavaScript 中,並不存在負數字面量。如果你寫 -1,實際上你是在對數字字面量 1 使用一元負號運算子 (-)。你可以給物件字面量設定一個數字字面量作為鍵,但你 不可以使用負數作為數字字面量鍵,因為不存在負數字面量。如果你寫 {-1: ""},在 JavaScript 中就是語法錯誤。

但由於鍵 -1 和 "-1" 是相同的,你仍然可以獲得完全相同的物件,方法是自己將其強制轉換為字串,或者使用計算屬性:

// x = { -1: "abc"}; // syntax error!
x = { "-1": "abc" }; // okay
x = { [-1]: "abc" }; // okay

而 TypeScript 接受這兩者作為與數字索引簽名匹配,因為它們會被強制轉換為正確類型的字串。

程式碼的 Playground 連結

答案#2

你不能在 JS 中使用負數作為索引,那是無效的語法,所以你可以這樣使用模板字面量:

遊樂場

type T = {
    [key: `${number}`]: string;
    [key2: `-${number}`]: string;
}

const foo: T = {0: 'string'}
const bar: T = {'-0': 'string'};
const baz: T = {a: 'string'}; // error