0

I have an array of classes, from which I'd like to init one class based on a static method (getSlug), and pair it with it correspondent consturctor param.

interface AnimalParams {
  name: string;
  color: string;
}

class Animal {
  params: AnimalParams;
  constructor(params: AnimalParams) {
    this.params = params;
  }
}

interface DogParams extends AnimalParams {
  species: string;
}

class Dog extends Animal {
  params: DogParams;
  constructor(params: DogParams) {
    super(params);
    this.params = params;
  }

  static slug(): string {
    return "dog";
  }
}

interface CatParams extends AnimalParams {
  sound: "meow";
}

class Cat extends Animal {
  params: CatParams;
  constructor(params: CatParams) {
    super(params);
    this.params = params;
  }

  static slug(): string {
    return "cat";
  }
}

const arr = [Dog, Cat];
const args = {
   dog: DogParams, 
   cat: CatParams
}

const animal = arr.find((x) => x.slug() === "dog");
if (!animal) {
  throw new Error("not found");
}
// union params type
// Property 'sound' is missing in type '{ name: string; color: string; species: string; }' but required in type 'CatParams'.

// new (params: DogParams & CatParams)
const a = new animal(args[animal.slug()]);

https://codesandbox.io/s/bold-james-15fsmq?file=/src/index.ts

Now when I try to instantiate a class, constructor params become unexpectedly an intersection of DogParams & CatParams, but it should be DogParams | CatParams union. slug will be known at runtime only. My main goal is to map constructor instances to their constructorParam types.

How can I fix the types?

bmz1
  • 188
  • 2
  • 8
  • Shouldn't the parameter type be only `DogParams`? You use `find` to find an animal that's a dog, after all. – kelsny Sep 22 '22 at 20:44
  • Does [this approach](https://tsplay.dev/WKR0ym) meet your needs? If so I will write up an answer; if not, what am I missing? (Please mention @jcalz to notify me if you reply) – jcalz Sep 23 '22 at 17:49
  • @jcalz thanks for the response, your solution is promising, however I don't know what the slug will be beforehand. I extended the TS [playground code](https://tsplay.dev/mx3AZN). – bmz1 Sep 24 '22 at 16:01
  • But I guess this can't be done in TS, so I accept your answer if you post it. – bmz1 Sep 24 '22 at 16:08
  • Hmm, I don't understand what you mean by "beforehand" exactly. Depending on when you're talking about this could range from easy to solve to impossible to solve. Your extended code could be fixed like [so](https://tsplay.dev/N5LXBw), but maybe you mean something else? Not sure. Let me know how to proceed. – jcalz Sep 24 '22 at 16:33
  • I mean by "beforehand", that slug will be known only at runtime, because it comes from an API request. So I think it can't be done. – bmz1 Sep 24 '22 at 16:47
  • Please mention @jcalz if you reply, or I will not be notified. You say you only know the slug at runtime, but I don't understand how you'd construct `new animal(arg)`; what `arg` would you use if you don't know what class `animal` is? Are you looking that up as well as the constructor? Could you provide a [mre] of your actual issue? I'm happy to write up the answer to the question as asked, but I'm puzzled because you apparently haven't demonstrated your underlying problem. Help! – jcalz Sep 24 '22 at 17:00
  • Like, maybe you need something like [this](https://tsplay.dev/wERayN) to correlate the generic slug name with the generic constructor param type? Maybe? – jcalz Sep 24 '22 at 17:22
  • @jcalz I appreciate your help, tomorrow I'll have time for posting an extended example which covers my use case. – bmz1 Sep 24 '22 at 17:37
  • @jcalz actually [this](https://tsplay.dev/wERayN) is what I was looking for. Thanks a lot. Please post it as an answer. – bmz1 Sep 24 '22 at 18:16
  • I will do so, but could you [edit] the question to motivate it? Presumably you have some code *like* that but it's just not compiling properly because of the typings. – jcalz Sep 24 '22 at 18:22
  • @jcalz done, pls post your answer – bmz1 Sep 25 '22 at 11:38
  • Sorry, I meant you should edit the *code* so that it shows something more like what you're doing. You can't safely write `new animal({species: "bulldog", ...})` and it doesn't make sense that you'd try to do that unless you know at compile time that `animal` is `Dog`. Instead you need to write `new animal(arg)` where `arg` and `animal` both depend on the same `slug` somehow. Maybe like [this](https://tsplay.dev/w1pJGW)? ... – jcalz Sep 25 '22 at 16:52
  • ... That has the same type error you're complaining about, but at least the algorithm does something reasonable at runtime. You don't have to use that exact example code, but you should really make it something that reflects your lack of compile-time knowledge. Does that make sense? – jcalz Sep 25 '22 at 16:53
  • @jcalz edited. Actually it turns out, that it's quite similar to this issue: https://stackoverflow.com/questions/55933800/typescript-unexpected-intersection Answered by you as well :D – bmz1 Sep 26 '22 at 09:59

0 Answers0