DEV Community

Cover image for TS Desafío - Merge
DevJoseManuel
DevJoseManuel

Posted on • Edited on

TS Desafío - Merge

En este post vamos a intentar resolver el siguiente desafío de TypeScript: Merge Challenge

Mezclar (merge) dos tipos en un nuevo tipo. Las claves del segundo tipo sobreescribirán a las claves del primero.

Más allá de un trabalenguas lo que primero que tenemos que entender es que lo que vamos a obtener es un objecto que representará a un tipo por lo que en la primera aproximación simplemente lo que haremos será indicarlo. Así si F y S representan a dos objetos (tipos) podemos escribir algo como lo siguiente::

type Merge<F, S> = {}
Enter fullscreen mode Exit fullscreen mode

De forma gráfica podemos representar a ambos tipos de la siguiente manera:

Los tipos  raw `F` endraw  and  raw `S` endraw

Ahora tenemos que mantener los atributos del primero y del segundo de los genéricos que estamos recibiendo. Esto se consigue utilizando el operador keyof. Pero, ¿cómo obtenemos los atributos de F? Pues simplemente escribiendo algo como lo siguiente:

[P in keyof F]
Enter fullscreen mode Exit fullscreen mode

que es algo así como pedirle a TypeScript que "cree un índice que conste de todos los atributos que forman parte del genérico F y almacenarlos en el genérico P eliminando los que estén duplicados".

 raw `[P in keyof F]` endraw

Pero, ¿cómo añadimos los atributos de S? Pues aquí es donde tenemos que utilizar el tipo de unión para obtener todos los atributos que hay en ambos objetos. Es más, tenemos que asignarle un valor a cada uno de estos atributos, pero como en este momento no podemos saber cuál es lo que hacemos será asignarle never porque de esta manera podemos asegurar que tenemos un tipo con un valor:

type Merge<F, S> = {
  [P in keyof F | keyof S]: never
}
Enter fullscreen mode Exit fullscreen mode

Por lo tanto, en este punto lo que tenemos es algo como lo que se muestra en la siguiente figura:

 raw `[P in keyof F | keyof S]` endraw

Ahora simplemente tenemos que determinar el valor que se asignará al atributo en cuestión. Según el enunciado del reto si el atributo pertenece al segundo genérico entonces ese será su valor mientras que si pertenece al primero será el del primero y en el caso de que no sea ninguno de los dos entonces devolveremos nunca.

¿Cómo podemos saber si un atributo pertenece a un objeto en TypeScript? Pues gracias al uso de la cláusula extends (https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html#in-extends-clause) de tal forma que, partiendo del tipo de datos que está formado por todos los atributos del segundo tipo S(que ya sabemos que se obtiene como keyof S) para saber si uno de los atributos pertenece al mismo, tendremos que escribir algo como lo siguiente:

[P in keyof F | keyof S]: P extends keyof S
Enter fullscreen mode Exit fullscreen mode

Es decir, hemos construido el tipo de datos que es la unión de los atributos de F y S y lo hemos llamado P. Ahora lo que hacemos simplemente es ver si el atributo P pertenece al conjunto de atributos S.

Ahora podemos hacer uso del Tipo Condicional que nos ofrece TypeScript de forma que si es un atributo de S lo mantendremos y en caso contrario comprobaremos si es del primer tipo.

¿Pero qué tenemos que hacer para mantener el tipo del atributo? Pues bien, basta con acceder a él en la tipado de S. ¿Cómo? Muy sencillo, utilizando el operador []. Es decir, si quieres acceder a un atributo de S escribirías algo como S['atributo']. Como el atributo que pasa la condición es P simplemente accedemos a él de tipo S. En caso de que no lo sea le asignaremos never:

type Merge<F, S> = {
  [P in keyof F | keyof S]: P extends keyof S ? S[P] : never
}
Enter fullscreen mode Exit fullscreen mode

Si repetimos este proceso pero esta vez viendo si el atributo está declarado en F para asignar su valor o en caso contrario lo que hacemos es asignarlo never ya que por lo que sea el atributo no forma parte de ninguno de los objetos (lo cual es imposible pero para que TypeScript no dé errores tenemos que completar el operador condicional).

Esto nos deja el siguiente resultado para el desafío:

type Merge<F, S> = {
  [P in keyof F | keyof S]: P extends keyof S
    ? S[P] 
    : P extends keyof F
      ? F[P]
      : never
}
Enter fullscreen mode Exit fullscreen mode

Ahora si vamos a ejecutar el resultado de nuestro challege para ver si tenemos errores o no veremos que todas las pruebas mostradas pasan correctamente por lo que la solución propuesta es correcta.

Checking the solution

Top comments (0)