0

I ran into a peculiar behaviour. Better explained by code below or following this typescript playground link

namespace Play {
    export interface BasicInterface {
        prop: any
    }

    export class Worker { 
        work(): BasicInterface {
            return { prop: "Hello" }
        }
    }

    export class Foo<T extends BasicInterface> {
        constructor(
            public worker: Worker,
            public workerCb: (workerResult: T) => any
        ) {}
        execute(): Promise<T> {
            return new Promise(res => {
                // Argument of type 'BasicInterface' is not assignable to parameter of type 'T'.
                res( this.workerCb(this.worker.work()) )
            }
        }    
    }
}
const foo = new Play.Foo<Play.BasicInterface>(new Play.Worker(), res => res)
foo.execute().then(res => console.log(res))

The problem occurs at line 20 saying that

Argument of type 'BasicInterface' is not assignable to parameter of type 'T'.

which is weird because T is defined as T extends BasicInterface in the Foo class declaration, so it should be fully compatible with BasicInterface.

So, question are: why is that and how to fix that?

UPDATE I'm even more puzzled now because TS seems to be fine with this which is the same(-ish?) thing or I don't see how it's much different...

namespace Play {
    export interface BasicInterface {
        prop: any
    }
    export class Implmentor implements BasicInterface {
        prop = "Hello"
    }
    export class Foo<T extends BasicInterface> {
        constructor(thingy: T) { }
    }
}

new Play.Foo(new Play.Implmentor())
new Play.Foo({prop: "Yay!"})
Nemoden
  • 8,816
  • 6
  • 41
  • 65

1 Answers1

2

This is a correct error.

Your class's contract says that this code is legal:

interface BetterInterface extends Play.BasicInterface {
    thing: string;
}

const foo = new Play.Foo<BetterInterface>(new Play.Worker(), res => res)
// Throws an exception because 'thing' is undefined
foo.execute().then(res => console.log(res.thing.toUpperCase))

But your implementation can't produce an arbitrary T, it just always produces BasicInterface.

See also Why can't I return a generic 'T' to satisfy a Partial<T>?

Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235