elderapo
elderapo

Reputation: 374

Problem with generic extending some class

Why do I get errors in e4, e5, e6? All of them match SomeClass type so they have to match generic RES.

class SomeClass {
  a: string;
  b: number;
  c: boolean;
}

class ChildOfSomeClass extends SomeClass {}

const someFunc1 = <RES extends SomeClass>() => {
  const e1: SomeClass = { a: "aaa", b: 123, c: true }; // ok
  const e2: SomeClass = new SomeClass(); // ok
  const e3: SomeClass = new ChildOfSomeClass(); // ok

  // Type '{ a: string; b: number; c: true; }' is not assignable to type 'RES'.ts(2322)
  const e4: RES = { a: "aaa", b: 123, c: true };

  // Type 'SomeClass' is not assignable to type 'RES'.ts(2322)
  const e5: RES = new SomeClass();

  // Type 'ChildOfSomeClass' is not assignable to type 'RES'.ts(2322)
  const e6: RES = new ChildOfSomeClass();
};

I eventually want someFunc1 to return RES | Promise<RES> but for the sake of the example, I simplified it.

Upvotes: 0

Views: 70

Answers (2)

p.s.w.g
p.s.w.g

Reputation: 149058

RES extends SomeClass means that the set of values described by type RES is a subset of the set of values described by type SomeClass. Imagine the following scenario:

class MyRES extends SomeClass {
   d: string;
}

This satisfies the condition RES extends SomeClass, however in none of the constructor cases you've provided do you assign d.

There are many ways to approach this problem, and the "correct" solution will depend on what exactly you're trying to accomplish with this method. For example, one solution would be to make your method take some constructor or converter method to create a RES from the known existing properties of SomeClass:

const someFunc1 = <RES extends SomeClass>(toRes: (SomeClass) => RES) => {
  const e: RES = toRes({ a: "aaa", b: 123, c: true }); // ok
};

Upvotes: 2

apokryfos
apokryfos

Reputation: 40700

This is because RES extends SomeClass. Imagine the following:

class SomeClass {
  a: string;
  b: number;
  c: boolean;
}

class ChildOfSomeClass extends SomeClass {}

class OtherChild extends SomeClass { d: string }

And imagine that RES is OtherChild (which does extend SomeClass) your code would then look like:

const someFunc1 = () => {
  const e1: SomeClass = { a: "aaa", b: 123, c: true }; // ok
  const e2: SomeClass = new SomeClass(); // ok
  const e3: SomeClass = new ChildOfSomeClass(); // ok


  const e4: OtherChild = { a: "aaa", b: 123, c: true };  // Not OK, OtherChild needs d

  // Downcasting SomeClass to OtherChild is not ok
  const e5: OtherChild = new SomeClass(); 

  const e6: OtherChild = new ChildOfSomeClass(); //This is especially not OK
};

You could solve this by just using SomeClass and then passing any child object will be ok.

Upvotes: 1

Related Questions