Home > other >  Enforce type-checking when abstract method returns generic
Enforce type-checking when abstract method returns generic

Time:01-24

I have an abstract base class that calls a child class method.

abstract class Base<T> {
  protected abstract getProps(): T;

  public process() {
    const props = this.getProps();
    // process props...
  }
}

I want to enforce type-checking on the return type of a child method.

interface ChildProps {
  prop1: string;
  prop2: string;
}

The following code doesn't produce any errors

class Child extends Base<ChildProps>{
  protected getProps() {
    return {
      prop1: 'prop1',
      prop2: 'prop2',
      otherdata: 'foo', // <---- no errors
    }
  }
}

but when I set the return type explicitly, the code fails to compile (as expected)

class Child extends Base<ChildProps>{
  protected getProps(): ChildProps {
    return {
      prop1: 'prop1',
      prop2: 'prop2',
      otherdata: 'foo', // <---- gives an error "prop does not exist"
    }
  }
}

CodePudding user response:

The type checking in your example works fine. The object literal has all the properties of ChildProps and they are of the correct type. We can see an error if we break this contract.


class Child extends Base<ChildProps>{
  protected getProps() {
//          ^^^^^^^^  Type '() => { prop1: string; prop2: number; }' 
//                    is not assignable to type '() => ChildProps'
    return {
      prop1: 'prop1',
      prop2: 0,
    }
  }
}

You are expecting excess property checks to produce an error. But those are only done at a few places; mainly when an object literal is assigned to a variable with an explicit type or when an object literal is returned from a function with an explicit type annotation.

When a sub-class implements a method, excess property checks are intentionally disabled. After all, the sub-class to supposed to extend the super-class.

As you noted in your question, an explicit type annotation for getProps will enable excess property checks because an object literal is returned from a function with explicit return type.

class Child extends Base<ChildProps>{
  protected getProps(): ChildProps {
    return {
      prop1: 'prop1',
      prop2: 'prop2',
      otherdata: 'foo', // <---- gives an error "prop does not exist"
    }
  }
}

This will be your solution to avoid excess properties for now. Maybe the TypeScript Team will implement exact types at some point which may solve this issue.

  • Related