millimoose
millimoose

Reputation: 39980

Why can't I capture the type of the parameters of Angular's FormGroup constructor?

I'm trying to make a well-typed wrapper function that ultimately calls new FormGroup(), and I want to pass through parameters to it without just copy-pasting the signature.

TypeScript however complains that the FormGroup class type cannot be used as the type argument to TS's ConstructorParameters:

Type FormGroup does not satisfy the constraint new (...args: any[]) => any.
Type FormGroup provides no match for the signature new (...args: any[]): any.

This can be reproduced by excerpting from the Angular .d.ts:

declare interface AbstractControl { }
declare interface ValidatorFn { }
declare interface AbstractControlOptions { }
declare interface AsyncValidatorFn { }

declare class FormGroup {
    constructor(
        controls: {[key: string]: AbstractControl}, 
        validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, 
        asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null
    );
}

type FormGroupParams = ConstructorParameters<FormGroup>; // <= Error occurs here

What gives? Why is something declared as a class with a constructor not newable to TS? And is there a way I can access this signature?

Upvotes: 0

Views: 410

Answers (1)

Daniel Klischies
Daniel Klischies

Reputation: 1135

The type signature that is implied by ConstructorParameters<T> (i.e. the presence of new (...args: any[]) => any) is checked only against the instance part of the class. However, the constructor is part of the static part of the class. The type checker does not see the presence of the constructor and hence gives you a type error. See the docs on this.

What you would need, however, would be a way of extracting from the static side to the instance part, as the following works as intended:

declare interface FormGroupConstructor {
    new (
        controls: {[key: string]: AbstractControl}, 
        validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, 
        asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null
    ): FormGroup;
}

type FormGroupParams = ConstructorParameters<FormGroupConstructor>

The interface I've declared here is the type of the constructor function. In other words, you want to access the ConstructorParameters of the type of the constructor. So, instead of

type FormGroupParams = ConstructorParameters<FormGroup>

you'd just do

type FormGroupParams = ConstructorParameters<typeof FormGroup>

Upvotes: 2

Related Questions