3

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?

yudai-nkt
  • 262
  • 2
  • 8

1 Answers1

0

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.

Aleksey L.
  • 35,047
  • 10
  • 74
  • 84
  • This technique can be used to check if type is interface or not. But not sure if it is useful))) – captain-yossarian from Ukraine Jan 04 '21 at 08:26
  • I actually checked the referenced issue, but had no idea that it was relevant, thank you. Still, my question remains; why is `interface Foobar extends Record { key: string };` valid? If `{ key: string }` in the expression is an interface (not a type alias), it doesn't have index signature and can't be a partial type of `Record`, can it? I might be missing something fundamental... – yudai-nkt Jan 04 '21 at 08:54
  • 1
    @yudai-nkt You're mixing "interface extension" https://www.typescriptlang.org/docs/handbook/interfaces.html#extending-interfaces with conditional types https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types – Aleksey L. Jan 04 '21 at 08:57
  • 1
    Ah, got it. Yes, I was overthinking things and somehow confused these two `extends`. Thanks again! – yudai-nkt Jan 04 '21 at 09:46