Is there a way to abstract over the type
{ 'k': number, [s: string]: any }
by creating a type alias T
such that T<'k', number>
results in the desired type?
Take a look at the following example:
function f(x: { 'k': number, [s: string]: any }) {} // ok
type T_no_params = { 'k': number, [s: string]: any }; // ok
type T_key_only<k extends string> = { [a in k]: number }; // ok
type T_value_only<V> = { 'k': V, [s: string]: any}; // ok
type T_key_and_index<k extends string, V> = { [a in k]: V, [s: string]: any };// ?
- Directly using
{ 'k': number, [s: string]: any}
as the type of the parameter in the functionf
is acceptable. - The indexed part
[s: string]: any
in a type alias works correctly - Using
k extends string
in a type alias also functions as expected - However, attempting to combine
k extends string
with[s: string]: any
in the same type alias results in a parse error.
One approach that seems to work is:
type HasKeyValue<K extends string, V> = { [s: string]: any } & { [S in K]: V }
But it is unclear why this does not flag extra properties as errors (the type on the right side of the &
should not allow objects with additional properties).
EDIT:
Despite the &
being the intersection operator, behaving akin to set-theoretic intersection, it does not handle extra properties in the same way, as evidenced by the following example:
function f(x: {a: number}){};
function g(y: {b: number}){};
function h(z: {a: number} & {b: number}){};
f({a: 42, b: 58}); // does not compile. {a: 42, b: 58} is not of type {a: number}
g({a: 42, b: 58}); // does not compile. {a: 42, b: 58} is not of type {b: number}
h({a: 42, b: 58}); // compiles!
In the above example, it appears that {a: 42, b: 58}
neither matches {a: number}
nor {b: number}
, yet it combines as {a: number} & {b: number}
. This deviates from set-theoretical intersection principles.
This inconsistency raises doubts about how intersecting a mapped type with { [s: string]: any }
could expand the type rather than restrict it.
I've come across similar inquiries like:
- Index signature for a mapped type in Typescript
- How do I add an index signature for a mapped type
Although they share a resemblance in name, they do not directly address the issue at hand.