TypeScript 索引签名实际上是什么意思? [英] What does a TypeScript index signature actually mean?

查看:25
本文介绍了TypeScript 索引签名实际上是什么意思?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经写了一段时间的 TypeScript,对索引签名的含义感到困惑.

I've been writing TypeScript for a while and am confused about what an index signature means.

例如,这段代码是合法的:

For example, this code is legal:

function fn(obj: { [x: string]: number }) {
    let n: number = obj.something;
}

但是这段代码,基本上做同样的事情,不是:

But this code, which does basically the same thing, isn't:

function fn(obj: { [x: string]: number }) {
    let p: { something: number } = obj;
}

这是一个错误吗?这是什么意思?

Is this a bug? What's the intended meaning of this?

推荐答案

您感到困惑是对的.索引签名有一些含义,它们的含义略有不同,具体取决于您询问的地点和方式.

You are right to be confused. Index signatures mean a few things, and they mean slightly different things depending where and how you ask.

首先,索引签名意味着类型中所有声明的属性必须具有兼容类型.

First, index signatures imply that all declared properties in the type must have a compatible type.

interface NotLegal {
    // Error, 'string' isn't assignable to 'number'
    x: string;
    [key: string]: number;
}

这是索引签名的一个定义方面——它们描述了一个具有不同属性键的对象,但所有这些键的类型一致.此规则可防止在通过间接访问访问属性时观察到错误类型:

This is a definitional aspect of index signatures -- that they describe an object with different property keys, but a consistent type across all those keys. This rule prevents an incorrect type from being observed when a property is accessed through an indirection:

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;
}

这是索引签名的一个关键用例:您有一组键,并且想要存储与该键关联的值.

This is a key use case for index signatures: You have some set of keys and you want to store a value associated with the key.

第三,索引签名暗示特别要求的任何属性的存在:

Third, index signatures imply the existence of any property you specifically ask for:

interface NameMap {
    [name: string]: number;
}
function getMyAge(ageLookup: NameMap) {
    // Inferred return type is 'number'
    return ageLookup["RyanC"];
}

因为 x["p"]x.p 在 JavaScript 中具有相同的行为,TypeScript 将它们等同对待:

Because x["p"] and x.p have identical behavior in JavaScript, TypeScript treats them equivalently:

// Equivalent
function getMyAge(ageLookup: NameMap) {
    return ageLookup.RyanC;
}

这与 TypeScript 看待数组的方式一致,即假定数组访问是在边界内进行的.索引签名也符合人体工程学,因为通常情况下,您有一组已知的可用密钥,无需进行任何额外检查:

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;
}

然而,索引签名并不暗示任何任意的、非特定的属性在涉及将带有索引签名的类型与带有声明的类型相关联时都会出现属性:

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;

这种分配在实践中不太可能是正确的!

This sort of assignment is very unlikely to be correct in practice!

这是 { [k: string]: any }any 本身之间的一个区别特征 - 您可以使用索引签名,但它不能像 any 那样代替任何类型.

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.

例如,这两个函数的运行时行为是相同的,但 TypeScript 只认为其中一个被错误调用:

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

总的来说,当您在类型上编写索引签名时,您是在说:

Overall, when you write an index signature on a type, you're saying:

  • 可以用任意键读/写这个对象
  • 当我通过任意键从此对象读取特定属性时,它始终存在
  • 出于类型兼容性的目的,此对象没有字面上的每个属性名称

这篇关于TypeScript 索引签名实际上是什么意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆