TypeScript's type system is unsound in places; you've found this issue in which type aliases but not interfaces are given implicit index signatures. Giving a type an implicit index signature is useful but unsafe in general. Consider:
const fooBar = { foo: "foo", bar: 123 };
const tFooBar: T = fooBar; // okay
const uFooBar: U = tFooBar; // okay?
const whoopsie = uFooBar.bar; // string at compile time, number at runtime?!
console.log(whoopsie);
The value fooBar
is a valid T
, because it has a foo
property of type string
. So you can assign it to tFooBar
. And then since TypeScript allows you to assign a value of type T
to a variable of type U
, you can assign tFooBar
to uFooBar
. And now the unsoundness is exposed if you read the bar
property of uFooBar
. It should be a string
according to U
, but it's a number
. Oops.
Implicit index signatures are useful because often functions require values with index signatures, and it's helpful for values whose known properties conform to the index signature to be accepted. So, we have this useful thing which can lead to type-unsafe behavior. What should be done?
Apparently the current rule for TypeScript is:
- object literals / anonymous types are given implicit index signatures
- type aliases are given implicit index signatures
- interfaces are NOT given implicit index signatures
Apparently this last is intentional and not a bug, according to this comment by @RyanCavanaugh:
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 the thought is that declaration merging might break interface-to-index-signature compatibility but type aliases can't. They're open to altering it, maybe, and if you have a compelling use case you might want to go to the Github issue and mention it.
Okay, hope that helps; good luck!
Link to code