Home > Net >  Allowing a class that inherits from an abstract class to return undefined
Allowing a class that inherits from an abstract class to return undefined

Time:05-25

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.

Reproducable example here

CodePudding user response:

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?

Option 1

As you already said in the post, you can explicitly set the return type of the function to foo | undefined.

Option 2

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"
}

Option 3

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;
}
  • Related