在 TypeScript中,可索引類型是指那些可以通過索引訪問其屬性值的類,通常情況下,這些類型被定義為對象或數組,用來模擬數組或字典的行為。可索引類型允許你定義一個接口來指定對象可以被哪些類型的鍵所索引,這個定義的接口有一個索引簽名,用于描述可以使用的索引類型及其對應的返回值類型。
1、基本索引類型語法
你可以通過索引簽名(index signatures)
來定義可索引類型。索引簽名使用 []
來表示,并且需要指定鍵的類型和值的類型。常見的鍵類型包括 string
和 number
。注:現在只支持字符串
和數字
.
- 定義鍵值為數字的索引類型
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"]都可以呢,后面的
字符串索引與數字索引的關系
中會說明
- 定義鍵值為字符串的索引類型
- 基礎字符串索引類型的示例
interface NumberDictionary {
[key: string]: number;
}
例子中NumberDictionary
是一個具有字符串索引的類型,并且每個索引對應的值都是數字類型。具體表示NumberDictionary
的屬性名稱為字符串類型,對應的屬性值為數字類型。
let _myDict: NumberDictionary = {"a":10, "b":20}
let _myValue:number = _myDict["a"]
console.log(_myValue); // 10
- 再實現個索引值類型為任意值的示例
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.property
和obj["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
類型。因為Dog
是Animal
的子類型,所以這是合法的
// 定義三個動物
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
,因此 id
和 name
屬性都合法。
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
接口既支持字符串索引也支持數字索引,并且值可以是 string
或 number
。
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
能夠強類型地描述對象、數組等數據結構。通過使用可索引類型,你可以更靈活地定義和操作那些具有動態結構的數據集合。這對于處理數據存儲、配置對象等場景非常有用。