Do you need help with a tricky situation? 😅
The Case:
Imagine a scenario where there's a main class involving multiple sub-classes:
// Main class
class Something <T> {
constructor (x: T) {
// ...
}
doSomething (value: T) {
// ...
}
}
// Subclasses
class AnotherThing extends Something<string> {}
class YetAnotherThing extends Something<number> {}
// Union of subclasses
type SomethingUnion = AnotherThing | YetAnotherThing;
Now let's say we have an object type that contains values of type SomethingUnion
...
type SomethingMap = {
[key: string]: SomethingUnion;
}
...and a mapped type that extracts the type parameter from each element within a given SomethingMap
(known as DataOf
):
// Extracts `T` from `Something<T>`.
type GetT<S extends Something<any>> = S extends Something<infer U> ? U : never
// Mapped type to extract type parameter from every subclass of `Something`.
type DataOf<T extends SomethingMap> = {
[K in keyof T]: GetT<T[K]>;
}
The Problem:
If you have a value someMap
(of type SomethingMap
) and another value someOtherMap
, which is of type DataOf
created from someMap
, and you wish to iterate over the entries of someMap
, TypeScript may infer the type of thing.doSomething(...)
's parameter to be never
. How can we make it match the type of valueToDoThingsWith
instead?
// Assume someMap was defined earlier as type `SomethingMap`
// and someOtherMap was established as having a type similar to `DataOf<typeof someMap>`
const entries = Object.entries(someMap); // [string, SomethingUnion][]
const mappedEntries = entries.map(([key, thing]) => {
const valueToDoThingsWith = someOtherMap[key] // string | number
// Here, `thing` has type `SomethingUnion`, but the parameter `value` resolves to `never`,
// conflicting with `valueToDoThingsWith` of type "string | number".
thing.doSomething(valueToDoThingsWith)
// ...
})
Is there a solution to this dilemma, ensuring that doSomething
accepts a parameter matching valueToDoThingsWith
's type?