I have a form which has three separate instances, but at its core has the same data with one or two different properties. I wanted to have a base form class which takes a generic argument to narrow the types and remove some controls where they are not needed, but I have been fighting Typescript for hours and am close to giving up and just using untyped forms. I have created a smaller example to show the problem: export type FormOne = { name: AbstractControl<string | null>; email: AbstractControl<string | null>; age: AbstractControl<number | null>; }; export type FormTwo = { name: AbstractControl<string | null>; email: AbstractControl<string | null>; }; export type FormThree = { name: AbstractControl<string | null>; email: AbstractControl<string | null>; location: AbstractControl<string | null>; }; export type BaseForm = FormOne | FormTwo | FormThree; I wanted to define my base class like so: @Directive() export abstract class BaseFormComponent<T extends BaseForm> { private readonly _formBuilder = inject(FormBuilder); public readonly baseForm = this._formBuilder.group<T>({ name: this._formBuilder.control<string | null>(''), email: this._formBuilder.control<string | null>(''), age: this._formBuilder.control<number | null>(0), location: this._formBuilder.control<string | null>(''), }); } but come across the TS2345 error: TS2345: Argument of type { name: FormControl<string | null>; email: FormControl<string | null>; age: FormControl<number | null>; location: FormControl<string | null>; } is not assignable to parameter of type T { name: FormControl<string | null>; email: FormControl<string | null>; age: FormControl<number | null>; location: FormControl<string | null>; } is assignable to the constraint of type T, but T could be instantiated with a different subtype of constraint BaseForm. This confuses me because there are no other subtypes of BaseForm than the three I have defined, and my understanding of union types is that BaseForm would resolve to: { name: AbstractControl<string | null>; email: AbstractControl<string | null>; age?: AbstractControl<number | null>; location?: AbstractControl<string | null>; } which should allow the base form as I defined it in the abstract class. The end goal would be to have components like: @Component({...}) export class FormTwoComponent extends BaseFormComponent<FormTwo> implements OnInit { public ngOnInit(): void { this.baseForm.removeControl('age'); this.baseForm.removeControl('location'); } } Now I realise that the component above has a form which doesn't conform to the given T until ngOnInit runs, and so this might cause compile-time problems of its own, but I haven't even got so far as defining the base class. Can anyone more versed in Typescript explain to me how I am going wrong, and how this could be done, if at all? StackBlitz project containing all code shown. Continue reading...