Hi everyone!
interface Thing {
name: string;
}
interface ThingMap {
[thingName: string]: Thing;
}
interface ThingMapClassA {
first: { name: 'first thing name' };
second: { name: 'second thing name' };
third: { name: 'third thing name' };
}
interface ThingMapClassB {
first: { name: 'first thing name' };
second: { name: 'second thing name' };
third: { name: 'third thing name' };
}
class Handler<T extends ThingMap> {}
const handler = new Handler<ThingMapClassA>();
I world like Handler to accept any class with properties (ideally at least one) of type Thing. But ThingMapClassA is not recognised. It leads to an error. Any suggestions?
The type
has a string index signature, meaning that if an object of that type has a property whose key is a
string, the value of that property will be aThing.If you have an anonymous object type, such as the type inferred from an object literal, and try to assign it to a type with an index signature, the compiler will helpfully give it an implicit index signature:
But implicit index signatures are not given to values of interface or class instance types. This is described at microsoft/TypeScript#15300. Observe:
And that's the problem you're running into.
ThingMapClassAandThingMapClassBare not assignable toThingMap, even though an anonymous object literal type equivalent to either one would be. So you'll need to change what you're doing.The easiest approach here is to change your constraint to be recursive. You don't need
Tto have a string index signature; you just want to know that its properties are assignable toThing. That can be expressed asusing the
Record<K, V>utility type.Record<keyof T, Thing>means "an object with the same keys asT, whose properties are of typeThing". So ifT extends Record<keyof T, Thing>, then we know that every property ofTis of typeString.So that gives us
and
as desired.
Playground link to code