¿Cómo escribir una utilidad de tipo de TypeScript que reduzca los tipos de unión en un objeto?
Tengo un montón de tipos generados (a través de GraphQL codegen) que se ven así:
type X = {
a?: Maybe<number>
b?: Maybe<Array<MyType> | MyType>
c?: Maybe<MyType2>
d?: Maybe<Array<MyType3> | MyType3>
}
// Maybe is:
// type Maybe<T> = T | null | undefined;
Para los campos b y d, el código generado hace lo correcto según la especificación de GraphQL (ver este problema), pero en mi código base específico, necesito transformar todos los tipos básicamente a esto:
type X2 = {
a?: Maybe<number>
b?: Maybe<Array<MyType>>
c?: Maybe<MyType2>
d?: Maybe<Array<MyType3>>
}
¿Cómo escribir una utilidad de tipo que haga esto?
(Manualmente, se haría así:)
type X2 = Omit<X, 'b' | 'd'> & {
b?: Maybe<Array<MyType>>;
d?: Maybe<Array<MyType3>>;
};
Respuesta #1
Es un poco complicado separar a los miembros del sindicato unos de otros, y no estoy 100% seguro sobre el caso de uso general, pero aquí hay una manera en la que podrías abordarlo:
type Xform<T> = {
[K in keyof T]: T[K] extends Maybe<infer V> ?
Extract<V, Array<any>>[number] extends infer R ?
T[K] extends Maybe<Array<R> | R> ?
Maybe<Array<R>>
: T[K] : T[K] : T[K]
}
type X2 = Xform<X>;
/* type X2 = {
a?: Maybe<number>;
b?: Maybe<MyType[]>;
c?: Maybe<MyType2>;
d?: Maybe<MyType3[]>;
} */
Básicamente, lo que estoy haciendo es: para cada clave K en las claves de T, estoy verificando el valor de la propiedad T[K]. Si alguna de mis pruebas falla, simplemente devolveré el valor de la propiedad sin cambios como T[K]. Las pruebas que estoy realizando: ¿T[K] es asignable a Maybe<V> para algún V? Si es así, encuentre los miembros de una unión V que sean Array<any> y obtenga su tipo de elemento, y guárdelo en un nuevo parámetro de tipo R (para conveniencia). Luego finalmente: ¿T[K] es asignable a Maybe<Array<R> | R>? Si es así, entonces devuelva Maybe<Array<R>>. Así que estamos buscando propiedades de la forma Maybe<Array<XYZ> | XYZ> y convirtiéndolas en Maybe<Array<XYZ>>, de lo contrario, las dejamos sin cambios.
Siempre es posible que surjan casos extremos; si alguno de los tipos MyType o XYZ son ellos mismos arreglos o si tienes uniones de otras cosas (por ejemplo, Maybe<MyType[] | MyType2>), pero con suerte esto al menos te da algo de orientación.
Enlace de Playground al código
