I have an abstract class that looks like this:
// step.ts
import { AllowedStepNames } from "./interfaces";
abstract class Step {
abstract nextStep(): AllowedStepNames | undefined
}
Which in turn uses a type in a seperate file:
// interfaces/index.ts
const stepList = {
foo: "bar",
bar: "baz"
};
export type AllowedStepNames = keyof typeof stepList;
I have a class that extends from it that looks like this:
// referral-reason.step.ts
export default class ReferralReasonStep extends Step {
nextStep() {
return this.params.reason === 'no-reason' ? 'foo' : undefined
}
}
However, the compiler throws an error:
Property 'nextStep' in type 'NotEligibleStep' is not assignable to the same property in base type 'Step'.
However, if I add the return type in my inherited class like so:
export default class ReferralReasonStep extends Step {
nextStep(): AllowedStepNames | undefined {
return this.params.reason === 'no-reason' ? 'foo' : undefined
}
}
This seems strange to me, because I'd expect the extended class to inherit the return type from the abstract class. Can someone tell me what's going on? Or am I doing something wrong?
In addition, if I put all my classes and types in the same file, the problem goes away.
I think I got the issue now:
TypeScript is automatically inferring the type string instead of foo | undefined to your function based on the return type and string is not assignable to the union AllowedStepNames.
What can you do to fix this?
As you already said in the post, you can explicitly set the return type of the function to foo | undefined.
The reason there was no error in the TypeScript playground is a difference in the tsconfig rules. I identified strictNullChecks to be the rule that changes this behavior. So depending on your project, you could enable this rule.
compilerOptions: {
strictNullChecks: "true"
}
By using as const you can tell the compiler to use the string literal value as the type instead of string.
nextStep() {
return this.params.reason === "no-reason" ? "foo" as const : undefined;
}