在 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
能够强类型地描述对象、数组等数据结构。通过使用可索引类型,你可以更灵活地定义和操作那些具有动态结构的数据集合。这对于处理数据存储、配置对象等场景非常有用。
网友评论