Closed yulric closed 5 years ago
This is a correct error. Parent
's contract claims that this call is legal
const p: Parent<{ nx: number }> = new Child();
p.getX<{}>({});
Child
's signature for getX
does not support that call, e.g. this code reads two undefined
s:
class Child extends Parent<{ nx: number }> {
getX(x: { nx: number, ax: number }): { nx: number, ax: number } {
return { nx: x.nx + x.ax, ax: 0 };
}
}
Sorry, I'm not sure what you mean by "this code reads two undefined
's"?
The call p.getX<{}>({});
passes in an empty object, so in the body of the method the two property accesses:
x.nx + x.ax
will both return undefined
because x
is empty.
I feel like you're trying to use U = T & { ax: number }
as a shortcut to define the U generic.
That construct is actually used to define a default value for the generic U when is not otherwise specified, that means that I could call p.getX<number>(4)
(accepted by the Parent
's contract).
The problem occurs because Child define that I cannot make that call anymore.
This code should show the error a bit better:
// let's change Child slightly keeping the same contract
// assume that this doesn't give any error
class Child extends Parent<{ nx: number }> {
getX(x: { nx: number, ax: number }): { nx: number, ax: number } {
return { nx: 12, ax: 10 };
}
}
const p: Person<{ nx: number }> = new Child();
console.log(p.getX<number>(4));
// this would not give any error, **the expected logged value is a number**
// but since p is actually an instance of Child is going to log me an object!!!
Remember that when you extends/implements you can only make your generics less specific (allows more values), this is why it fails when you use your code where the generic U becomes more specific (allow less values, in your case it allows only the value { nx: number }
).
I will show two example to show what I just said:
abstract class Parent {
abstract getX<A extends number>(x: A): A;
}
class Child extends Parent {
// ERROR! 5 is more specific than number
// Type 'number' is not assignable to type '5'.
getX<A extends 5>(x: A): A {
return x;
}
}
abstract class Parent {
abstract getX<A extends 5>(x: A): A;
}
class Child extends Parent {
// IT WORKS! number is less specific than 5
getX<A extends number>(x: A): A {
return x;
}
}
In any case I think you meant to do this:
abstract class Parent<T> {
abstract getX(x: T & { ax: number }): T & { ax: number }
}
class Child extends Parent<{ nx: number }> {
getX(x: { nx: number, ax: number }): { nx: number, ax: number } {
return x;
}
}
This all makes a lot of sense. Thanks a lot, closing the issue.
TypeScript Version: Typescript@3.3.1
Search Terms: generic abstract class method
Code
Expected behavior: No Errors
Actual behavior: Property 'getX' in type 'Child' is not assignable to the same property in base type 'Parent<{ nx: number; }>'. Type '(x: { nx: number; ax: number; }) => { nx: number; ax: number; }' is not assignable to type '<U = { nx: number; } & { ax: number; }>(x: U) => U'. Types of parameters 'x' and 'x' are incompatible. Type 'U' is not assignable to type '{ nx: number; ax: number; }'
Playground Link: https://www.typescriptlang.org/play/index.html#src=abstract%20class%20Parent%3CT%3E%20%7B%0D%0A%20%20abstract%20getX%3CU%20%3D%20T%20%26%20%7B%20ax%3A%20number%20%7D%3E(x%3A%20U)%3A%20U%0D%0A%7D%0D%0A%0D%0Aclass%20Child%20extends%20Parent%3C%7B%20nx%3A%20number%20%7D%3E%20%7B%0D%0A%20%20getX(x%3A%20%7B%20nx%3A%20number%2C%20ax%3A%20number%20%7D)%3A%20%7B%20nx%3A%20number%2C%20ax%3A%20number%20%7D%20%7B%0D%0A%20%20%20%20return%20x%3B%0D%0A%20%20%7D%0D%0A%7D%0D%0A
Related Issues: