I've come across Typescript's conditional : T extends U ? X : Y
syntax and found it to be quite powerful. However, I haven't been able to figure out a way to specify the return type based on an attribute of a class or within the function itself. Consider this simple class:
class X {
members: number[] = [];
returnRoman = false;
first(): number|string {
if (!this.returnRoman) {
return members[0];
} else {
return numberToRomanNumeral(members[0]);
}
}
}
Is there a way to constrain the return type to just number or string for better type-checking? The issue arises from a scenario like this:
class Base {
x: number = 0;
}
class Sub extends Base {
y: number = 0;
}
class FilterIterator {
members: Base[] = [];
filterClass: typeof Base = undefined;
class first(): Base {
for (const m of this.members) {
if (this.filterClass !== undefined && !(m instanceof this.filterClass)) {
continue;
}
return m;
}
}
}
const fi = FilterIterator();
fi.filterClass = Sub
fi.members = [new Sub(), new Sub()];
const s = fi.first();
if (s.y > 0) {
console.log('s is moving up in the world!');
}
The problem here is that fi.first()
is expected to return Base
, causing errors when attempting to access y
which is not part of the Base class. One workaround could be:
const s = fi.first() as Sub;
However, if we had a situation like this instead:
class FilterIterator extends Base {
...
filterClass = FilterIterator;
}
then you end up with code like:
const firstOfFirstOfFirst = (((fi.first() as FilterIterator).first()
as FilterIterator).first() as FilterIterator);
This becomes even more complicated when dealing with generators that can return other generators, as declaring types within for...of
loops isn't currently supported. It also shifts the responsibility of determining the returned value to the consuming software rather than having FilterIterator or Base handle it.
If you have any solutions or improvements, I'd love to hear them.