假设我们有一个用户管理系统,用户的信息通过以下格式存储:
interface User {
name: string;
phone: number;
address: {
country: string;
province: string;
city: string;
postalCode: number;
}
}
这时,我们想获取用户 address 的类型,最直接的办法是将address抽取为单独的类型。
interface Address {
country: string;
province: string;
city: string;
postalCode: number;
}
interface User {
name: string;
phone: number;
address: Address;
}
这段代码如果是我们自己业务可控的当然没问题,但如果它来源于第三方,我们没法直接修改代码怎么办呢?
这时候我们可以借助 ts 的索引访问类型(Indexed Access Types)来查找属性值的类型。
索引访问类型形如 T[K],有点类似 js 对象的属性访问。需要注意的是这里的 P 是类型(对象类型、数组类型或元组类型)而不是值,否则会报错。 Key 是一个属性名(字符串或数字)或索引签名。
const address = "address";
// 报错
// 'address' refers to a value, but is being used as a type here. Did you mean 'typeof address'?
type Address = User[address];
// ok,'address'是User类型的property,是一个字面量类型
type Address = User['address'];
以上通过特定的字面量属性获取类型是索引访问类型的最基本用法,下面介绍一些高级用法。
联合类型作为索引访问
可以使用联合类型作为索引,获取多个属性值的联合类型。比如:
interface User {
name: string;
phone: number;
address: {
country: string;
province: string;
city: string;
postalCode: number;
}
}
type NameOrPhone = 'name' | 'phone';
type Username1 = User[NameOrPhone]; // string | number
type Username2 = User['name' | 'phone']; // string | number
结合 keyof 操作符,可以获取对象所有属性的联合类型:
type Username = User[keyof User];
// 等价于
type Username = string | number | {
country: string;
province: string;
city: string;
postalCode: number;
}
深度访问
还是上面 User 的例子,不同的系统设计,address.postalCode可能是 number 也有可能是 string 类型。如果我们想获取它的类型怎么办呢?类似 js 对象的属性访问,索引访问类型也能一层一层的深度访问。
interface User {
name: string;
phone: number;
address: {
country: string;
province: string;
city: string;
postalCode: number;
}
}
type Address = User['address']['postalCode']; // number
T[string]和 T[number]
索引访问不一定要使用明确的字面量类型,它还允许使用 T[string]或 T[number]这样的特殊形式。 对于对象类型来说,如果定义了 index 签名,可以通过 T[string]获取属性值的所有类型的联合类型。
从数组类型中提取元素类型:
type StringArray = string[];
// 提取数组元素的类型
type ElementType = StringArray[number]; // string
- number:表示数组的索引类型,用于提取数组元素的类型。
从元组类型中提取元素类型:
type Tuple = [string, number, boolean];
// 提取第一个元素的类型
type FirstElement = Tuple[0]; // string
// 提取第二个元素的类型
type SecondElement = Tuple[1]; // number
使用索引签名提取类型: 如果对象类型使用了索引签名,你可以通过索引访问类型提取索引签名的值类型。
type Dictionary = {
[key: string]: number;
};
// 提取索引签名的值类型
type ValueType = Dictionary[string]; // number
结合 keyof 操作符:
type Person = {
name: string;
age: number;
isActive: boolean;
};
// 获取所有键的联合类型
type Keys = keyof Person; // "name" | "age" | "isActive"
// 提取所有属性类型的联合类型
type ValueTypes = Person[Keys]; // string | number | boolean
总结
索引访问类型在以下场景中非常有用:
-
动态提取属性类型:根据属性名动态获取类型。
-
类型安全的访问:在泛型或工具类型中使用,确保类型安全。
-
简化复杂类型操作:减少重复代码,提高代码可读性。