I have the following code:
abstract class BaseToken {}
class OpenParen extends BaseToken {
public static assert(t: BaseToken): OpenParen {
if (t instanceof OpenParen) {
return t;
} else {
throw typeAssertionError;
}
}
}
class CloseParen extends BaseToken {
public static assert(t: BaseToken): CloseParen {
if (t instanceof CloseParen) {
return t;
} else {
throw typeAssertionError;
}
}
}
The assert functions can be used to check whether a given instance of BaseToken is in fact a certain specialization of it.
const t: BaseToken = getTokenFromSomewhere();
const oparen = OpenParen.assert(t); // type of oparen is OpenParen, exception if not
As there are many more classes other than OpenParen and CloseParen derived from BaseToken, this is obviously a lot of boilerplate. I'd like to move the implementation of assert into the base class. The following does not work:
abstract class BaseToken {
public static assert<T>(t: BaseToken): T {
if (t instanceof T) { // T used as a value here
return t;
} else {
throw typeAssertionError;
}
}
}
class OpenParen extends BaseToken {}
class CloseParen extends BaseToken {}
The problem is that on the one hand, this doesn't compile as instanceof requires a value, not a class, and on the other hand, this approach does not bind T to the subclass type, so OpenParen.assert would still be a generic.
I believe that I could achieve this in C++ with the Curiously recurring template pattern (CRTP), but that doesn't work in TypeScript. Is there some other trick to do the same thing?