Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type is assignable to the constraint of type 'Type', but 'Type' could be instantiated with a different subtype of constraint 'Type'

There are a few questions already asked like this. But I think this a little more specific.

Please see this example:

interface Body {
  legs: number;
}

interface Kingdom {
  animalia: {
    sound: string; // meow..
    body: Body;
  };
}

function GetBody<
  Category extends keyof Kingdom,
  B extends Kingdom[Category]["body"]
>(cat: Category): B {
  // stuff..
  return { legs: 4 }; // <==== Error!
}

// called like: GetBody('animalia').legs

The Error says:

Type '{ legs: number; }' is not assignable to type 'B'.
  '{ legs: number; }' is assignable to the constraint of type 'B', 
  but 'B' could be instantiated with a different subtype of constraint 'Body'.ts(2322)

What am I doing it wrong here? How can I fix this?

like image 699
Wajahath Avatar asked Apr 11 '20 17:04

Wajahath


1 Answers

Your problem is that B extends Kingdom[Category]["body"] does not mean that B is Kingdom[Category]["body"], instead it means that B must be assignable to Kingdom[Category]["body"].

So, as @Aluan pointed out, a type like {legs: number, eyes: 2} is perfectly valid for B. {legs: number, eyes: 2} does extend Kingdom[Category]["body"]. This means that { legs: 4 }, the object you return, couldn't be enough. It would be perfect if B were exactly Kingdom[Category]["body"], but this is not always the case.

In your situation, there is no need for a generic. Indeed, it's wrong to use one because you need exactly Kingdom[Category]["body"]:

function GetBody<
  Category extends keyof Kingdom,
>(cat: Category): Kingdom[Category]["body"] {
  return { legs: 4 };
}

Note: there is already a TS interface called Body. Change the name of yours.

like image 83
Andrea Simone Costa Avatar answered Nov 20 '22 16:11

Andrea Simone Costa