Search code examples
typescriptinterfaceconditional-typesmapped-types

Interface cannot extend mapped type in conditional type


While debugging my program, I noticed that the following example yields a compile error (playground).

type Foo = {key: string};
interface Bar {key: string};

type Baz = Foo extends Record<string, unknown>? any: never;
type Qux = Bar extends Record<string, unknown>? any: never;

const baz: Baz = 0;
const qux: Qux = 0; // Type 'number' is not assignable to type 'never'.

It seems interfaces cannot extend Record<string, unknown> whereas types can. I know there are several differences between types and interfaces in TypeScript, and I suspect the fact that mapped types cannot be used in interfaces might explain the behavior. I cannot fully understand why this map type restriction results in Qux being never even if that's the case, though.

Moreover, interface Foobar extends Record<string, unknown> { key: string }; is a valid interface definition, which makes the error more confusing to me.

Could anyone help me understand this error?


Solution

  • That's because type aliases have implicit index signature, but interfaces don't.

    If you'll add index signature to the interface - the Qux will result in any:

    interface Bar { 
        key: string;
        [p: string]: string;
    };
    

    Playground

    More info here:

    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.