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'?
I'd say your generics are inappropriately scoped. If BaseApplicant
is a specific class with a generic handleApplicantData
method, then you're saying that callers of handleApplicantData
can specify any type argument for T
that they want. Meaning someone could call new Applicant().handleApplicantData(null)
if they wanted. But you don't want or expect BaseApplicant
or any of its subclasses to be that flexible.
If you want implementers to specify the type argument, then you really want BaseApplicant
to be a generic class with a specific handleApplicantData
method. That is, move the generic from handleApplicantData
to Baseapplicant
:
abstract class BaseApplicant<T> {
abstract handleApplicantData(applicantData: T, isPrimaryApplicant: boolean): void;
}
Now instead of having your subclass try to make handleApplicantData
generic somehow (which led to you trying and failing to specify the type argument as ApplicantModel
, even though that was just the name of a new generic type parameter), your subclasses can choose what T
is:
class Applicant extends BaseApplicant<ApplicantModel> {
handleApplicantData(applicantData: ApplicantModel): void {
this.processApplicant(applicantData);
}
processApplicant(applicant: ApplicantModel): void {
console.log(applicant.name);
}
}
This compiles as desired. An Applicant
can only handle ApplicantModel
. Some other subclass of BaseApplicant
could handle some other data.