'Can't assign correlated union to object
I'm trying to use the "correlated union" changes in TS 4.6 to let me keep some factory functions typed when they're used by a generic factory (and their returned values, which I don't really show below because I already run into trouble with the factory functions).
I can't make it happy with the assignment at the end of this example:
type Factory<T> = () => T
type FactoryTypes = {
foo: string
thing: { size: number }
}
type NamedFactory<K extends keyof FactoryTypes = keyof FactoryTypes> = {
[P in K]: {
name: P
factory: Factory<FactoryTypes[P]>
}
}[K]
const factories: { [N in keyof FactoryTypes]: NamedFactory<N> } = {
foo: { name: "foo", factory: () => "hi" },
thing: { name: "thing", factory: () => ({ size: 456 }) },
}
function addFactory<N extends keyof FactoryTypes>(fv: NamedFactory<N>) {
factories[fv.name] = fv
}
That last line gives me:
Type 'NamedFactory<N>' is not assignable to type '{ foo: { name: "foo"; factory: Factory<string>; }; thing: { name: "thing"; factory: Factory<{ size: number; }>; }; }[N]'.
Type '{ name: N; factory: Factory<FactoryTypes[N]>; }' is not assignable to type 'never'.
The intersection '{ name: "foo"; factory: Factory<string>; } & { name: "thing"; factory: Factory<{ size: number; }>; }' was reduced to 'never' because property 'name' has conflicting types in some constituents.
Of course in real code I'd also have some way to call a factory given a name, returning a value returned by one of the named factory functions.
How can I change this code so TS is happy with storing the factory functions in an object, retaining the factory return type information?
Solution 1:[1]
The issue that you are having is caused by the fv parameter type not being assignable to the factory object's properties. The reason why this is happening is because NamedFactory property values' types do not match, so when you attempt to assign a NamedFactory type's property - TypeScript attempts to make a union out of its property value types (resulting in a type that just contains the name property type), and because this union does not apply to the minimum requirements of the factories object (as all of its properties contain more than name), this argument is no longer applicable - hence it is set to never.
The fix is relatively simple, all you need to do is to directly infer the property value type from the factories const itself. This can be done by replacing fv: NamedFactory<N> with fv: typeof factories[N] which will infer the full property value type from factories directly and will not have to do the union step:
function addFactory<N extends keyof FactoryTypes>(fv: typeof factories[N]) {
factories[fv.name] = fv;
}
Playground link here.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
