I am implementing a unique set of types to enable angular form typing:
import { FormArray, FormControl, FormGroup } from '@angular/forms';
export type DeepFormArray<T, U extends boolean | undefined = undefined> = T extends Array<any>
? never
: T extends string | number | boolean
? FormArray<FormControl<T>>
: U extends true
? FormArray<FormControl<T>>
: FormArray<DeepFormGroup<T>>;
export type DeepFormGroup<T> = T extends object
? FormGroup<{
[P in keyof T]: T[P] extends Array<infer K>
? DeepFormArray<K>
: T[P] extends object
? DeepFormGroup<T[P]>
: FormControl<T[P] | null>;
}>
: never;
My goal is to apply these types to a type with a string literal property acting as a type identifier. Here's an example scenario:
interface A {
name: 'A'
}
interface B {
name: 'B'
}
/** expected output
* {name: 'A' | 'B'}
*/
type C = A | B;
interface D {
prop: C[];
}
These are combined as follows:
// The initial example was not precise for my requirements,
// however, the corrections proposed by @jcalz resolved the issue
/** expected output
* FormGroup<
* {name: FormControl<'A' | null>}
* > | FormGroup<
* {name: FormControl<'B' | null>}
* >
*/
type OldActualType = DeepFormGroup<C>;
type OldNeededType = FormGroup<{name: FormControl<'A' | 'B' | null>}>
// The accurate rendition of my problem
/** expected output
* FormGroup<{
* prop: FormArray<FormGroup<{
* name: FormControl<"A" | null>;
* }>> | FormArray<FormGroup<{
* name: FormControl<"B" | null>;
* }>>;
* }>
*/
type ActualType = DeepFormGroup<D>;
type NeededType = FormGroup<{
prop: FormArray<FormGroup<{
name: FormControl<"A" | "B" | null>;
}>>;
}>
It appears that TypeScript does not create a new type for C
, but interprets it as two variants. When C
is used, all paths are explored. Is there a way to adjust DeepFormGroup
or the definition of ActualType
so that the inferred type matches NeededType
? I am using TypeScript 4.8. Here is a link to a TypeScript playground