What does a TypeScript index signature actually mean?

淺唱寂寞╮ 提交于 2019-12-19 17:34:24

问题


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?


回答1:


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

Second, index signatures allow writing to any index with a compatible type.

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

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

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

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!

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

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

  • It's OK to read/write this object with an arbitrary key
  • When I read a specific property from this object through arbitrary key, it's always present
  • This object doesn't have literally every property name in existence for the purposes of type compatibility


来源:https://stackoverflow.com/questions/58458308/what-does-a-typescript-index-signature-actually-mean

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!