1

I have classes whose constructors require objects which form a tagged union:

interface Info {
  x: string;
}

interface AInfo extends Info {
  x: 'a';
}

class A {
  constructor(info: AInfo) {}
}

interface BInfo extends Info {
  x: 'b';
}

class B {
  constructor(info: BInfo) {}
}

type AllInfos = AInfo|BInfo;

I've omitted class internals and constructor initialization. How can I type this factory function to correlate the type of object created based on the info passed in?

const factory = (info: AllInfos) => {
  switch (info.x) {
    case 'a': return new A(info);
    case 'b': return new B(info);
    // ...
  }
};

const obj = factory(myInfo); // type of obj is A|B, I'd like it to vary by myInfo

1 Answer 1

1

The simplest way to do it is by using overloads:

function factory(info: AInfo): A
function factory(info: BInfo): B
function factory(info: AllInfos) {
  switch (info.x) {
    case 'a': return new A(info);
    case 'b': return new B(info);
    // ...
  }
};

const objA = factory({ x: 'a' }); // A
const objB = factory({ x: 'b' }); // B

Playground Link

If there are a lot of options in the union, you can get creative and create a distributive conditional type to create a signature for each member in the union, and then make it into an overloaded signature using UnionToIntersection (see here):

type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

type Factory<T> = T extends new (p: infer P) => infer R ? (a: P) => R : never
type FactoryParameters<T> = T extends new (p: infer P) => infer R  ? P : never
type AllClasses = typeof A | typeof B

const factory = ((info: FactoryParameters<AllClasses>) => {
  switch (info.x) {
    case 'a': return new A(info);
    case 'b': return new B(info);
  }
}) as UnionToIntersection<Factory<AllClasses>>;

const objA = factory({ x: 'a' }); // A
const objB = factory({ x: 'b' }); // B

Playground Link

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.