I need to implement a factory function that creates an instance of a subtype – the factory function accepts the type discriminator plus a subset of properties that should be populated via Partial<T>.
This is a minimal code example:
type TypeA = {
discriminator: 'A';
aProp: string;
};
type TypeB = {
discriminator: 'B';
bProp: string;
};
type TypeX = TypeA | TypeB; // either of the two types
type TypeDiscriminator = TypeX['discriminator']; // "A" | "B"
function createInstance(discriminator: TypeDiscriminator, partial: Partial<TypeX>) {
switch (discriminator) {
case 'A':
return { ...partial, discriminator: 'A' } as TypeA;
case 'B':
return { ...partial, discriminator: 'B' } as TypeB;
default:
throw Error();
}
}
// Question: This should infer type to 'TypeA' (but it is TypeA | TypeB instead)
export const a = createInstance('A', { aProp: 'foobar' });
// this works, but is actually invalid (aProp does not exist on TypeB and should be caught as error)
export const b = createInstance('B', { aProp: 'foobar' }) as TypeB;
How would this be properly be implemented, without alterting the parameter list of createInstance?
>Solution :
This works:
function createInstance<D extends TypeDiscriminator>(
discriminator: D,
partial: Partial<Extract<TypeX, { discriminator: D }>>
) {}
It picks the right type based on the discriminator given.