Rya*_*ugh 8 typescript index-signature
我写TypeScript已有一段时间了,对索引签名的含义感到困惑。
例如,此代码是合法的:
function fn(obj: { [x: string]: number }) {
let n: number = obj.something;
}
Run Code Online (Sandbox Code Playgroud)
但是,执行相同操作的代码不是:
function fn(obj: { [x: string]: number }) {
let p: { something: number } = obj;
}
Run Code Online (Sandbox Code Playgroud)
这是错误吗?这是什么意思?
你很困惑。索引签名意味着几件事情,它们的含义也有所不同,具体取决于您询问的地点和方式。
首先,索引签名暗示该类型中所有声明的属性必须具有兼容的type。
interface NotLegal {
// Error, 'string' isn't assignable to 'number'
x: string;
[key: string]: number;
}
Run Code Online (Sandbox Code Playgroud)
这是索引签名的定义方面–它们描述具有不同属性键的对象,但所有这些键之间的类型都是一致的。此规则防止通过间接访问属性时观察到不正确的类型:
function fn(obj: NotLegal) {
// 'n' would have a 'string' value
const n: number = obj[String.fromCharCode(120)];
}
Run Code Online (Sandbox Code Playgroud)
其次,索引签名允许写入具有兼容类型的任何索引。
interface NameMap {
[name: string]: number;
}
function setAge(ageLookup: NameMap, name: string, age: number) {
ageLookup[name] = age;
}
Run Code Online (Sandbox Code Playgroud)
这是索引签名的密钥用例:您有一组密钥,并且想要存储与该密钥关联的值。
第三,索引签名暗示您明确要求的任何属性的存在:
interface NameMap {
[name: string]: number;
}
function getMyAge(ageLookup: NameMap) {
// Inferred return type is 'number'
return ageLookup["RyanC"];
}
Run Code Online (Sandbox Code Playgroud)
因为x["p"]
和x.p
在JavaScript中具有相同的行为,所以TypeScript等效地对待它们:
// Equivalent
function getMyAge(ageLookup: NameMap) {
return ageLookup.RyanC;
}
Run Code Online (Sandbox Code Playgroud)
This is consistent with how TypeScript views arrays, which is that array access is assumed to be in-bounds. It's also ergonomic for index signatures because, very commonly, you have a known set of keys available and don't need to do any additional checking:
interface NameMap {
[name: string]: number;
}
function getAges(ageLookup: NameMap) {
const ages = [];
for (const k of Object.keys(ageLookup)) {
ages.push(ageLookup[k]);
}
return ages;
}
Run Code Online (Sandbox Code Playgroud)
However, index signatures don't imply that any arbitrary, unspecific property will be present when it comes to relating a type with an index signature to a type with declared properties:
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;
Run Code Online (Sandbox Code Playgroud)
This sort of assignment is very unlikely to be correct in practice!
This is a distinguishing feature between { [k: string]: any }
and any
itself - you can read and write properties of any kind on an object with an index signature, but it can't be used in place of any type whatsoever like any
can.
Each of these behaviors is individually very justifiable, but taken as a whole, some inconsistencies are observable.
For example, these two functions are identical in terms of their runtime behavior, but TypeScript only considers one of them to have been called incorrectly:
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
Run Code Online (Sandbox Code Playgroud)
Overall, when you write an index signature on a type, you're saying:
归档时间: |
|
查看次数: |
338 次 |
最近记录: |