typescript - TypeScript 索引签名实际上意味着什么?
问题描述
我写 TypeScript 已经有一段时间了,对索引签名的含义感到困惑。
例如,这段代码是合法的:
function fn(obj: { [x: string]: number }) {
let n: number = obj.something;
}
但是这个基本上做同样事情的代码不是:
function fn(obj: { [x: string]: number }) {
let p: { something: number } = obj;
}
这是一个错误吗?这是什么意思?
解决方案
你感到困惑是对的。索引签名意味着几件事,它们的含义略有不同,具体取决于您询问的地点和方式。
首先,索引签名意味着类型中所有声明的属性都必须具有兼容的类型。
interface NotLegal {
// Error, 'string' isn't assignable to 'number'
x: string;
[key: string]: number;
}
这是索引签名的定义方面——它们描述具有不同属性键的对象,但所有这些键的类型一致。此规则可防止在通过间接访问属性时观察到不正确的类型:
function fn(obj: NotLegal) {
// 'n' would have a 'string' value
const n: number = obj[String.fromCharCode(120)];
}
其次,索引签名允许写入具有兼容类型的任何索引。
interface NameMap {
[name: string]: number;
}
function setAge(ageLookup: NameMap, name: string, age: number) {
ageLookup[name] = age;
}
这是索引签名的一个关键用例:您有一组密钥,并且您想要存储与密钥关联的值。
第三,索引签名暗示存在您特别要求的任何属性:
interface NameMap {
[name: string]: number;
}
function getMyAge(ageLookup: NameMap) {
// Inferred return type is 'number'
return ageLookup["RyanC"];
}
因为x["p"]
和x.p
在 JavaScript 中具有相同的行为,TypeScript 将它们等同对待:
// Equivalent
function getMyAge(ageLookup: NameMap) {
return ageLookup.RyanC;
}
这与 TypeScript 查看数组的方式一致,即假定数组访问是在界内的。索引签名也符合人体工程学,因为通常情况下,您有一组已知的可用密钥,并且不需要进行任何额外的检查:
interface NameMap {
[name: string]: number;
}
function getAges(ageLookup: NameMap) {
const ages = [];
for (const k of Object.keys(ageLookup)) {
ages.push(ageLookup[k]);
}
return ages;
}
但是,索引签名并不意味着在将具有索引签名的类型与具有声明的属性的类型相关联时会出现任何任意的、非特定的属性:
interface Point {
x: number;
y: number;
}
interface NameMap {
[name: string]: number;
}
const m: NameMap = {};
// Not OK, which is good, because p.x is undefined
const p: Point = m;
这种分配在实践中不太可能是正确的!
{ [k: string]: any }
这是它和它本身之间的一个显着特征any
- 您可以在具有索引签名的对象上读取和写入任何类型的属性,但它不能用于代替任何类型的任何类型any
。
这些行为中的每一个都是单独非常合理的,但作为一个整体,一些不一致是可以观察到的。
例如,这两个函数在运行时行为方面是相同的,但 TypeScript 只认为其中一个被错误地调用:
interface Point {
x: number;
y: number;
}
interface NameMap {
[name: string]: number;
}
function A(x: NameMap) {
console.log(x.y);
}
function B(x: Point) {
console.log(x.y);
}
const m: NameMap = { };
A(m); // OK
B(m); // Error
总体而言,当您在类型上编写索引签名时,您是在说:
- 可以用任意键读/写这个对象
- 当我通过任意键从该对象中读取特定属性时,它始终存在
- 出于类型兼容性的目的,该对象实际上并没有存在每个属性名称
推荐阅读
- gcc - 将本机节点模块编译到 Android 时无法链接库。使用 -fPIC 标志时出现 -fPIC 问题重新编译
- javascript - Sourcemap 在 Sentry 中无效或不可解析
- guid - ProductCode (GUID) 用于为 AutoCAD 自动加载 DLL
- java - Tomcat 优雅关闭
- node.js - 如何在 Service 构造函数中对 Controller 进行单元测试和模拟 @InjectModel
- tomcat - Tomcat 服务器的 JVM 可用内存未返回给操作系统
- javascript - 如何打印我放入的文件的文件名在一个?
- c++ - 使用 C++ 中的 ASCII 代码将输入从小写转换为大写
- r - 如何在R中使用knn查找重要变量
- c# - 当该表中没有行时,C# sqlite 删除行不起作用