Search code examples
typescript

TypeScript doesn't assign with interface while assign with object literal


Why first assignment to 'a' doesn't work while second does.

interface A {
    [k: string]: string | number;
}

interface B {
    p: string;
    d: number;
}

const b: B = {p:"", d:0};
const a: A = b;

const b2 = {p:"", d:0};
const a2: A = b2;

Solution

  • In your latter assignment, b2 is of type {p: string; d: number}, which structurally matches B, so one might expect that the former assignment for b would work as well. Why does TypeScript infer an index signature for the object literal and not for a structurally-identical interface?

    It looks like implicit index signatures for object literals and type aliases have been available since TypeScript v2.0, but they don't happen for interfaces. Again: why?

    Well, according to @RyanCavanaugh's comment on microsoft/TypeScript#15300:

    Just to fill people in, this behavior is currently by design. Because interfaces can be augmented by additional declarations but type aliases can't, it's "safer" (heavy quotes on that one) to infer an implicit index signature for type aliases than for interfaces. But we'll consider doing it for interfaces as well if that seems to make sense.

    So it sounds like something they might consider implementing. If you care about it, you might want to head over to that issue and give it a 👍, or comment with a description of your use case if it's new and compelling.

    UPDATE: this is now considered permanently by design and will not be changed. The last comment in microsoft/TypeScript#15300 says:

    Changing the behavior at this point would honestly be an incredibly disruptive breaking change without much concrete upside. For clarity, I'm going to close and lock this - interfaces should declare an explicit index signature if they intend to be indexed.

    So this is how TypeScript will work for the foreseeable future.


    NOTE WELL: in the specific case of an index signature where the property type is any, as in

    { [k: string]: any }
    

    the compiler treats this as assignable even to/from interfaces without index signatures. It's a special-cased "no-op", see this comment in microsoft/TypeScript#41476.