This particular kind of type does not actually expand upon the other.
If we consider { a: number }
, it is apparent that it indeed expands upon { [key: string]: number }
when we think about calling a function with a signature like this:
foo(arg: { [key: string]: number })
In this case, it would be acceptable to provide an argument of type { a: number }
. Any operation performed by foo
using its argument could also apply to something of type { a: number }
.
However, if you have:
foo(arg: (value: { [key: string]: number }) => any)
It would be inappropriate to pass an argument of type (value: { a: number }) => any
, as the argument for foo
requires a function capable of accepting an object with any string keys. Consider how foo
might be implemented like this:
foo(arg: (value: { [key: string]: number }) => any) {
arg({ b: 7 });
}
In this scenario, the code would not be valid if arg
has type (value: { a: number }) => any
.
To delve deeper into this topic, what we are discussing here is known as type contravariance.