TypeScript:聲明可索引類型

在 TypeScript中,可索引類型是指那些可以通過索引訪問其屬性值的類,通常情況下,這些類型被定義為對象或數組,用來模擬數組或字典的行為。可索引類型允許你定義一個接口來指定對象可以被哪些類型的鍵所索引,這個定義的接口有一個索引簽名,用于描述可以使用的索引類型及其對應的返回值類型。

1、基本索引類型語法
你可以通過索引簽名(index signatures)來定義可索引類型。索引簽名使用 []來表示,并且需要指定鍵的類型和值的類型。常見的鍵類型包括 stringnumber。注:現在只支持字符串數字.

  • 定義鍵值為數字的索引類型
interface StringArray {
  [index: number]: string;
}

例子中StringArray是一個具有數字索引的類型,并且每個索引對應的值都是字符串類型,這意味著任何數字都可以作為索引,并且對應的值必須是數字。

let _myArray: StringArray = ["Bob", "Fred"]
let _myStr: string = _myArray[0];
console.log(_myStr); //Bob
//或
_myStr = _myArray["0"]
console.log(_myStr); //Bob

為什么_myArray[0]和_myArray["0"]都可以呢,后面的字符串索引與數字索引的關系中會說明

  • 定義鍵值為字符串的索引類型
  1. 基礎字符串索引類型的示例
interface NumberDictionary {
  [key: string]: number;
}

例子中NumberDictionary是一個具有字符串索引的類型,并且每個索引對應的值都是數字類型。具體表示NumberDictionary的屬性名稱為字符串類型,對應的屬性值為數字類型。

let _myDict: NumberDictionary = {"a":10, "b":20}
let _myValue:number = _myDict["a"]
console.log(_myValue); // 10
  1. 再實現個索引值類型為任意值的示例
interface StringDictionary {
    [key: string]: any; // 任何字符串都可以作為鍵,值的類型是任意類型
}

在這個例子中,StringDictionary 接口定義了一個字符串索引簽名,這意味著任何字符串都可以作為鍵,并且對應的值可以是任意類型。

let _anyDict: StringDictionary = {name: "Alice", age: 25, location: "Wonderland" }
console.log(_anyDict["name"]); //輸出:Alice
console.log(_anyDict.name); //輸出:Alice
console.log(_anyDict["age"]); //輸出:25
console.log(_anyDict.age); //輸出:25
console.log(_anyDict["location"]); //輸出:Wonderland
console.log(_anyDict.location); //輸出:Wonderland

注:我們發現["name"].name的取值方式都可獲得正確的索引值,表明字符串索引類型聲明了obj.propertyobj["property"]兩種形式都可以獲取索引值

2、字符串索引與數字索引的關系
聲明索引類型時可以同時定義字符串和數字索引簽名,但 TypeScript 要求數字索引的返回類型必須是字符串索引返回類型的子類型,這是因為在JavaScript中,數字索引會首先被轉換為字符串鍵。

例如:定義了一個數字索引類型,用100(一個number)去獲取索引值,這時等同于使用"100"(一個string)去索引。

interface Animal {
    name: string;
}

interface Dog extends Animal {
    breed: string;
}

interface AnimalDictionary {
    [index: number]: Dog;
    [key: string]: Animal
}

在這里,數字索引number返回的是Dog類型,而字符串索引string返回的是Animal類型。因為DogAnimal的子類型,所以這是合法的

// 定義三個動物
let _dog_1: Dog = {
    name: "小白",
    breed: "京巴"
}
let _dog_2: Dog = {
    name: "小黑",
    breed: "柴犬"
}
let _animal_1: Animal = {
    name: "花花"
}
  • 第一種使用方式
let _animalInfo = {
    animal1: _dog_1, 
    100:_dog_1, 
    animal2: _dog_2, 
    200:_dog_2,
    animal3:_animal_1,
    300:_animal_1
}

console.log(_animalInfo["animal1"]); //輸出:{ name: '小白', breed: '京巴' }
console.log(_animalInfo[100]); //輸出:{ name: '小白', breed: '京巴' }
console.log(_animalInfo["animal3"]); //輸出:{ name: '花花' }
console.log(_animalInfo[300]); //輸出:{ name: '花花' }
  • 第二種使用方式
let _animalArr = [_dog_1, _dog_2, _animal_1]
console.log(_animalArr[0]); //輸出:{ name: '小白', breed: '京巴' }
console.log(_animalArr[1]); //輸出:{ name: '小黑', breed: '柴犬' }
console.log(_animalArr[2]); //輸出:{ name: '花花' }

3.索引簽名與屬性的關系
當在一個接口中同時定義了索引簽名和具體屬性時,所有具體屬性的類型必須符合索引簽名的約束。

  • 索引簽名與具體屬性
    當一個接口既有索引簽名,又有具體的屬性時,索引簽名的類型會影響具體屬性的類型,反之亦然。具體來說,所有具體屬性的類型必須兼容索引簽名的返回類型。

錯誤聲明方式:

interface MyDictionary {
    [key: string]: number;
    length: number; // 合法,屬性類型與索引簽名一致
    name: string // 非法,屬性類型與索引簽名類型沖突,`name`的類型與索引類型返回值的類型不匹配
    // 類型“string”的屬性“name”不能賦給“string”索引類型“number”。
}

在上面的例子中,length屬性是合法的,因為它的類型number與索引簽名[key: string]: number類型一致。但是name: string屬性則會報錯,因為string類型與索引簽名返回的number類型不兼容。

正確聲明方式:

interface MyDictionary {
    [key: string]: any;
    //或
    //[key: string]: number | string;
    length: number; 
    name: string; 
}
let _myInfo: MyDictionary = {length: 180, name: "bob", city: "北京", age: 24}
console.log(_myInfo["length"]); //輸出:180
console.log(_myInfo["name"]); //輸出:bob
console.log(_myInfo["city"]); //輸出:北京
console.log(_myInfo["age"]); //輸出:24
  • 具體屬性可以細化類型
    盡管索引簽名限制了屬性的類型,但具體的屬性可以是索引簽名的類型的子類型。因此,具體屬性可以比索引簽名的類型更加精確。
interface MyDictionary {
  [key: string]: number;
  specialProperty: 42;  // 具體屬性是索引簽名類型的子類型
}

在這個例子中,specialProperty 的類型是字面量類型 42,它是 number 類型的一個子類型,因此這種定義是合法的。

  • 混合使用索引簽名和屬性
    在一些場景下,具體屬性和索引簽名可以混合使用,但需要謹慎處理類型的一致性。如果你希望某些屬性具有不同的類型,可以通過類型聯合來實現
interface MyDictionary {
  [key: string]: number | string;
  id: number;
  name: string;
}

在這個例子中,[key: string]: number | string允許所有屬性的值既可以是 number 也可以是 string,因此 idname 屬性都合法。

4.索結合使用多種索引簽名
可以在一個接口中定義多個索引簽名,只要它們的鍵類型不同:

interface MixedDictionary {
    [key: string]: string | number;
    [index: number]: string;
}
  
let mixed: MixedDictionary = {
    0: 'zero',
    one: 1,
    two: 'two'
};

console.log(mixed[0]);
console.log(mixed["one"]);

在這個例子中,MixedDictionary 接口既支持字符串索引也支持數字索引,并且值可以是 stringnumber

5.索只讀索引簽名
如果你希望索引簽名是只讀的,可以使用 readonly 關鍵字,防止了給索引賦值:

interface ReadonlyStringDictionary {
  readonly [key: string]: string;
}

let roObj: ReadonlyStringDictionary = {
  prop: "value"
};

// roObj.prop = "newValue"; // 錯誤:不能修改只讀屬性
interface ReadonlyStringArray {
    readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error:你不能設置myArray[2],因為索引簽名是只讀的。

總結

  • 數字索引類型:[index: number]: Type
  • 字符串索引類型:[key: string]: Type
  • 數字索引類型的返回值必須是字符串索引類型的子類型。
  • 索引簽名定義了對象中屬性的類型規則,并且所有屬性都必須與索引簽名類型兼容。

這些特性使 TypeScript 能夠強類型地描述對象、數組等數據結構。通過使用可索引類型,你可以更靈活地定義和操作那些具有動態結構的數據集合。這對于處理數據存儲、配置對象等場景非常有用。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容