It seems that defining an abstract function with generic return type is not possible with typescript (I am using ts 3.2.4) or I am missing something.
For instance:
abstract class Foo {
abstract func<T>() : T;
}
class Bazz extends Foo {
func() { return 1; }
}
will give an error:
TS2416: Property 'func' in type 'Bazz' is not assignable to the same property in base type 'Foo'. Type '() => number' is not assignable to type '() => T'. Type 'number' is not assignable to type 'T'.
So, is it possible to define an abstract function that will have the generic return type?
Edit:
So, it turns out that using unknown work as expected and it can be used as the return type in an abstract function. But, let's consider this:
abstract class Foo{
getFuncResult() {
return this.func();
}
abstract func() : unknown;
}
class Bazz extends Foo {
func() { return 1; }
}
let foo: Foo = new Bazz();
foo.func(); // unknown
let bazz = new Bazz()
bazz.func() // number
bazz.getFuncResult() // unknown
I would expect that the getFuncResult on the Bazz will have the same type signature as derived func (number). So, this is more a question about type inference for Typescript.
We can return any type of value from the function or nothing at all from the function in TypeScript. Some of the return types is a string, number, object or any, etc. If we do not return the expected value from the function, then we will have an error and exception.
You can never, in no context, change an int from a overridden method into a float in the overriding method. The only special case that is allowed is return type covariance.
TypeScript has supported abstract classes since 2015, which provides compiler errors if you try to instantiate that class. TypeScript 4.2 adds support for declaring that the constructor function is abstract.
TypeScript has the ability to define classes as abstract. This means they cannot be instantiated directly; only nonabstract subclasses can be.
The problem is where you put the type parameter. If the type parameter is on the method, then that method must be generic in derived class. The contract that is specified by that method means we must always be able to call it with any T
(ie the caller decides with what T
to call). So this code need to work:
let a: Foo = new Bazz()
a.fun<string>() // method is generic caller decides `T`
You want to place the type parameter on the class, and fix the type parameter when inheriting in Bazz
abstract class Foo <T>{
abstract func() : T;
}
class Bazz extends Foo<number> {
func() { return 1; }
}
let foo: Foo<number> = new Bazz();
foo.func(); //ok result is numebr
let fooStr: Foo<string> = new Bazz() // error, different T
Edit
If you don't care what the return type is in the base class and you just want the derived class to create the method, you could use unknown
as the return type. If you have a reference to the abstract class and invoke the method it will return unknown
if you have a reference to a derived class it will return the apropriate type:
abstract class Foo{
abstract func() : unknown;
}
class Bazz extends Foo {
func() { return 1; }
}
let foo: Foo = new Bazz();
foo.func(); // unknown
let bazz = new Bazz()
bazz.func() // number
Edit
With regard to using func
in another function and expecting the type of that to be relative to func
typescript will not help you there, at least not by default. By default getFuncResult
will be typed with what information is known in the base class. Without using a class type parameter (and I do think you should look into that) a solution is to use a type query on the polymorphic this
type. This will type the return relative to whatever the current class is but you will need to use a type assertion inside the function to get things to work:
abstract class Foo{
getFuncResult() : ReturnType<this['func']> {
return this.func() as any;
}
abstract func() : unknown;
}
class Bazz extends Foo {
func() { return 1; }
}
let foo: Foo = new Bazz();
foo.func(); // unknown
let bazz = new Bazz()
bazz.func() // number
bazz.getFuncResult() // number
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With