Search code examples
javascripttypescriptgenerics

This type parameter might need an `extends` constraint in typescript


Consider these classes and model in typescript.

class ApplicantModel {
  name: string = '';
}

abstract class BaseApplicant {
  abstract handleApplicantData<T>(applicantData: T, isPrimaryApplicant: boolean): void;
}

class Applicant extends BaseApplicant {
  handleApplicantData<ApplicantModel>(applicantData: ApplicantModel): void {
    // Error: This type parameter might need an `extends ApplicantModel` constraint.
    this.processApplicant(applicantData);
  }

  processApplicant(applicant: ApplicantModel): void {
    console.log(applicant.name);
  }
}

I'm getting error:

Argument of type 'ApplicantModel' is not assignable to parameter of type 'ApplicantModel'.(2345)
input.tsx(10, 23): This type parameter might need an `extends globalThis.ApplicantModel` constraint.

Why I need to use extends constraint. Why Argument of type 'ApplicantModel' is not assignable to parameter of type 'ApplicantModel'?

Playground


Solution

  • In your case here:

    handleApplicantData<ApplicantModel>(applicantData: ApplicantModel): void {}
    

    ApplicantModel refers to the name of the generic not the type, as
    @Palladium02 mentions.

    What you want to do is this:

    handleApplicantData<T extends ApplicantModel>(applicantData: T): void {}
    

    But since that throws an error as well you would have to do this in the BaseApplicant class:

    abstract handleApplicantData<T extends ApplicantModel>(
      applicantData: T,
      isPrimaryApplicant: boolean
    ): void;
    

    Or if you want to keep it flexible:

    abstract handleApplicantData(
      applicantData: unknown,
      isPrimaryApplicant: boolean
    ): void;
    

    This would still allow you to declare the function as generic in the subclass.

    Final code would be:

    class ApplicantModel {
      name: string = '';
    }
    
    abstract class BaseApplicant {
      abstract handleApplicantData(applicantData: unknown, isPrimaryApplicant: boolean): void;
    }
    
    class Applicant extends BaseApplicant {
      handleApplicantData<T extends ApplicantModel>(applicantData: T): void {
        this.processApplicant(applicantData);
      }
    
      processApplicant(applicant: ApplicantModel): void {
        console.log(applicant.name);
      }
    }