Reputation: 270
interface Foo<T> {
getValue(): T;
}
class Foo1 implements Foo<string> {
__value: string;
constructor(value: string) {
this.__value = value;
}
getValue(): string {
return this.__value;
}
}
class Foo2 implements Foo<number> {
__value: number;
constructor(value: number) {
this.__value = value;
}
getValue(): number {
return this.__value;
}
}
class Bar<T extends Foo1 | Foo2> {
private __foo: T;
constructor(foo: T) {
this.__foo = foo;
}
getValueOfFoo() {
return this.__foo.getValue();
}
}
const bar1 = new Bar(new Foo1('hello'));
const val1: string = bar1.getValueOfFoo();
const bar2 = new Bar(new Foo2(12345));
const val2: number = bar2.getValueOfFoo();
I expected the return values of 42 and 45 line to be string
and number
but they aren't.
How can I define the type of method that can returns proper type depends on object type be provided thru constructor?
Upvotes: 2
Views: 46
Reputation: 6994
The reason why it is currently not working as expected is, that your generic constraint is defined as T extends Foo1 | Foo2
. Typescript does not allow narrowing of generic unions. See this issue #13995.
The gist of the issue is this comment from one of the typescript maintainers:
TL;DR from design discussion: It's somewhat obvious what the "right" thing to do is, but would require a large rework of how we treat type parameters, with concordant negative perf impacts, without a corresponding large positive impact on the actual user-facing behavior side.
If new patterns emerge that make this more frequently problematic, we can take another look.
A quick way to solve your issue at hand is the avoid the generic union like this, not sure if it will fit your actual use case:
class Bar<T> {
private __foo: Foo<T>;
constructor(foo: Foo<T>) {
this.__foo = foo;
}
getValueOfFoo() {
return this.__foo.getValue();
}
}
Upvotes: 3
Reputation: 2180
I ran the following piece of code (your code) and see the correct result.
const bar1 = new Bar(new Foo1('hello'));
const val1: string = <string>bar1.getValueOfFoo();
console.log(typeof(val1))
const bar2 = new Bar(new Foo2(12345));
const val2: number = <number>bar2.getValueOfFoo();
console.log(typeof(val2))
Output on console:
string
number
Upvotes: 0