typescript - ¿Cómo combinar dos parciales en un objeto completo?
Tengo una interfaz que requiere todas las propiedades. Sin embargo, crear el objeto es un proceso de dos pasos. Por lo tanto, planeo crear 2 versiones Partial del objeto, y luego combinarlas para cumplir con la interfaz no parcial. Ejemplo:
interface IComplete {
a: string,
b: string
}
const part1: Partial<IComplete> = {
a: 'hello'
}
const part2: Partial<IComplete> = {
b: 'world'
}
const together: IComplete = {
...part1,
...part2
}
Aunque together siempre está completo, el compilador se queja:
Type '{ a?: string; b?: string; }' is not assignable to type 'IComplete'.
Property 'a' is optional in type '{ a?: string; b?: string; }' but required in type 'IComplete'.
Patio de recreo
¿Existe una manera recomendada de lograr esto? Es importante para mi caso que la interfaz IComplete no sea parcial, es decir, que las propiedades a y b nunca sean nulas o indefinidas.
Respuesta #1
La respuesta de @RyanCavanaugh es buena. Una forma de dejar que la inferencia de tipos haga su trabajo mientras se siguen obteniendo los beneficios del compilador asegurando que part1 y part2 sean Partial<IComplete> es usar una función auxiliar como se muestra a continuación:
interface IComplete {
a: string,
b: string,
c: (x: string) => number,
d: (x: number) => string
}
// helper function
const asPartialIComplete = <T extends Partial<IComplete>>(t: T) => t;
const part1 = asPartialIComplete({
a: 'hello',
c: (x) => x.length
})
const part2 = asPartialIComplete({
b: 'world',
d: (x) => x + ""
});
const together: IComplete = {
...part1,
...part2
}
En lo anterior, tanto part1 como part2 están restringidos por asPartialIComplete para ser objetos válidos Partial<IComplete> (por lo que los parámetros de los métodos c y d se infieren como string y number respectivamente). Pero los tipos de part1 y part2 son lo suficientemente específicos para que el compilador se dé cuenta de que typeof part1 & typeof part2 es IComplete.
Espero que eso también ayude. ¡Buena suerte!
Respuesta #2
La forma más fácil sería eliminar las anotaciones de tipo y dejar que la inferencia de tipos haga su trabajo:
interface IComplete {
a: string,
b: string
}
const part1 = {
a: 'hello'
}
const part2 = {
b: 'world'
}
const together: IComplete = {
...part1,
...part2
}
Esto tiene ciertos efectos perjudiciales, por ejemplo, los parámetros en las expresiones de función en part1 o part2 no obtendrán tipos inferidos.
En su lugar, puedes usar Pick - desafortunadamente, tienes que escribir las claves que estás seleccionando de cada tipo al usar este enfoque:
interface IComplete {
a: string,
b: string,
c: number
}
const part1: Pick<IComplete, "a"> = {
a: 'hello'
}
const part2: Pick<IComplete, "b" | "c"> = {
b: 'world',
c: 43
}
const together: IComplete = {
...part1,
...part2
}
