Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstract function return type in typescript

Tags:

typescript

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.

like image 215
negyxo Avatar asked Feb 13 '19 07:02

negyxo


People also ask

Can a function return a type 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.

Can you change return type of abstract method?

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.

Is there an abstract class in TypeScript?

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.

Does TypeScript have abstract methods?

TypeScript has the ability to define classes as abstract. This means they cannot be instantiated directly; only nonabstract subclasses can be.


1 Answers

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
like image 91
Titian Cernicova-Dragomir Avatar answered Oct 17 '22 07:10

Titian Cernicova-Dragomir