Es hora de continuar (y esperar completar) nuestro tutorial de TypeScript.Si se perdió las publicaciones anteriores que escribimos sobre TypeScript, aquí: nuestra introducción inicial en TypeScript y la primera parte de este tutorial en el que explico el ejemplo de JavaScript con el que trabajamos y los pasos que seguimos para mejorarlo parcialmente.Hoy terminaremos nuestro ejemplo completando todo lo que falta.Específicamente, primero veremos cómo crear tipos que sean versiones parciales de otros tipos existentes.Luego veremos cómo introducir adecuadamente las acciones de una tienda Rodux utilizando sindicatos de tipo y discutiremos las ventajas ofrecidas por los sindicatos de tipo.Y finalmente, le mostraré cómo crear una función polimórfica cuyo tipo de retorno depende de sus argumentos.
Una breve revisión de lo que he hecho hasta ahora … en la primera parte del tutorial que utilicé (parte) de una tienda Redux que tomé del contenido de Neli como ejemplo de trabajo. Todo comenzó como un código JavaScript simple, que tuvo que mejorarse al agregar tipos concretos que lo hacen más robusto e inteligible. Por lo tanto, por ejemplo, definimos los siguientes tipos: type PostId = número; Escriba día = cadena; Escriba post = {id: postid; Títulos: cadena; Autor: cadena; Día día; Estado: cadena; ISsticky: booleano; }; Type state = {publicaciones: diccionario ; Días: Diccionario ; }; Lo que nos ayudó a comprender, de un vistazo, el tipo de información con la que funciona nuestra tienda. En este caso particular, por ejemplo, podemos ver que la condición de nuestra aplicación almacena dos cosas: una lista de publicaciones (que hemos indexado por su postid) y una estructura llamada días que, dado un cierto día, devuelve una lista de Identificadores post. También podemos ver los atributos (y sus tipos específicos) que encontraremos en un objeto post.
Una vez que se han definido estos tipos, hemos editado todas las funciones de nuestro ejemplo para usarlas. Esta simple tarea ha transformado las firmas de las funciones opacas de javascript: // selectores function getPost (state, id) {…} function getPostsInday (state, día) {…} // Función de acciones RecibeneneWpost (post) { …} función UpdatePost (postID, atributos) {…} // Reductor Función Reductor (Estado, Action) {…} En firmas autoexplicitas del tipo de función: // Función de selectores getPost (estados, id : postid): post | Undefinado {…} function getPostsInday (estado: estado, día: día): postid [] {…} // Función de acciones RecibeneneWpost (POST: POST): Any {…} Función UpdatePost (PostId: PostId, Atributos: any): any any {…} // reductor de la función reductor (estado: estado, acción: any): estados {…} getPostsInday La función es un muy buen ejemplo de cuánto mecanografiará la calidad de su código . Si miras la contraparte de JavaScript, realmente no sabes qué devolverá esa característica. Claro, su nombre podría sugerir el tipo de resultado (es una lista de publicaciones, mayo de ID de publicación). Esta situación se puede mejorar mejor llamando cosas (getIdsofpostsinday, por ejemplo), pero no hay nada como los tipos concretos para limpiar cualquier duda: postid [].
Entonces, ahora que eres consciente del estado actual de las cosas, es hora de seguir adelante y remediar todo lo que omití la semana pasada. Específicamente, sabemos que necesitamos introducir los atributos de atributo de la función UpdatePost y debemos definir los tipos que tendrán nuestras acciones (tenga en cuenta que en el descuento, el atributo de acción está en este momento). Cómo escribir un objeto cuyos atributos son un subconjunto de otro objeto para calentarnos de algo simple. La función UpdatePost genera una acción que indica nuestra intención de actualizar ciertos atributos de una ID de publicación determinada. Así es como se ve: function UpdatePost (PostId: PostId, Attributes: Any): Any {return {type: ‘update_post’, postid, atributes,}; } Y así es como el reductor utiliza la acción para actualizar la publicación de nuestra tienda: reductor (estado: estado, acción: any): state {// … switch (action.type) {// … Houses ‘update_post’: if (! state.posts [action.postid]) {return state; } const post = {… state.posts [Action.Postid], … Action.Attributes,}; devolver {…}; } //
¿Pero quiénes son exactamente atrobios a una acción? Bueno, soy claramente algo similar a una publicación, porque deberían sobrescribir los atributos que podemos encontrar en una publicación: Tipo UpdatePostction = {Tipo: ‘update_post’; PostId: número; Atributos: Post; }; Pero si intentamos usar esto, veremos que no funciona: consisten: post = {id: 1, título: ‘Título’, autor: ‘Ruth’, Día: ‘2020-10-01’, Estado: ‘ Borrador ‘, issticky: falso,}; Constation: updatePostction = {type: ‘update_post’, postid: 1, atributos: {autor: ‘toni’,},}; Porque no queremos que los atributos sean una publicación en sí misma; Queremos ser un subconjunto de atributos post (es decir, queremos especificar solo aquellos atributos de un objeto post que sobrescribiremos). Para resolver este problema, use solo el tipo de utilidad parcial: type updatePostction = {type: ‘update_post’ ; PostId: número; Atributos: parcial ; }; ¡Y eso es! ¿O es eso? Atributos del filtro explícito El fragmento anterior sigue siendo defectuoso, ya que puede obtener algunos errores de rodadura que el compilador TypeScript no verifica. Es por eso: la acción que señala una actualización de la publicación tiene dos argumentos, una ID de publicación y el conjunto de atributos que queremos actualizar. Una vez que tenemos la acción preparada, el reductor es responsable de anular la publicación existente con los nuevos valores:
const post = {… state.posts [Action.Postid], … Action.Attributes,};Y esta es precisamente la parte defectuosa de nuestro código;Es posible que el atributo PostId de la acción tenga una identificación de ID y el ID de atributo en los atributos para tener una identificación diferente: Constion: UpdatePostction = {type: ‘update_post’, postid: 1, atributos: {id: 2, autor, autor : ‘Toni’,},};Obviamente, esta es una acción válida y, por lo tanto, TypeScript no desencadena errores, pero sabemos que no debería serlo.El atributo de identificación en los atributos (si está presente) y el atributo PostId debe tener el mismo valor, de lo contrario tenemos una acción incoherente.Nuestro tipo de acción es imprecisa porque nos permite definir una situación que debería ser imposible … Entonces, ¿cómo podemos solucionar esto?Muy fácil: cambie solo este tipo, por lo que este escenario que debería ser imposible de ser imposible.
La primera solución en la que pensé es la siguiente: Elimine el atributo PostId de la acción y agregue la ID a los atributos atributos: type updatePostction = {type: ‘update_post’; Atributos: parcial ; }; Función UpdatePost (postID: PostId, Attributes: Parcial ): UpdatePostction {return {type: ‘update_post’, atributes: {… atributes, id: postID,},}; } Entonces, actualice el reductor para usar Action.Attributes.id en lugar de Action.Postid para encontrar y sobrescribir la publicación existente. Desafortunadamente, esta solución no es ideal, porque los atributos son una “publicación parcial”, ¿recuerdas? Esto significa que, en teoría, el atributo de identificación puede o no estar en el objeto de los atributos. Por supuesto, sabemos que estará allí, porque nosotros somos los que generamos la acción … pero nuestros tipos siguen siendo imprecisos. Si en el futuro alguien cambia la función UpdatePost y no se asegura de que los atributos incluyan POSTID, la acción resultante sería válida según TypeScript, pero nuestro código no funcionaría: Const Workingction: UpdatePostction = {type: ‘update_post’, atributos: { Id: 1, autor: ‘toni’,},}; Const fallingction: updatePostction = {type: ‘update_post’, atributes: {autor: ‘toni’,},}; Entonces, si queremos que TypeScript nos proteja, debemos ser lo más precisos posible cuando especifiquemos los tipos y nos aseguramos de que hagan imposibles los estados imposibles. En vista de todo esto, solo tenemos dos opciones disponibles:
Si tenemos un atributo PostId en acción (como lo hicimos al principio), entonces el objeto Atributos no debe contener un atributo de identificación.
Si, por otro lado, la acción no tiene un atributo PostId, entonces los atributos deben contener un atributo de identificación. La primera solución se puede especificar fácilmente utilizando otro tipo de utilidad, omitir, que nos permite crear un nuevo tipo eliminando los atributos De un tipo existente:
Escriba updatePostAction = {type: ‘update_post’;PostId: postID, atributos: parcial <omit ;};que funciona de acuerdo con las expectativas: const workngction: updatePostction = {type: ‘update_post’, postid: 1, atributos: {autor: ‘toni’,},};Const fallingction: updatePostction = {type: ‘update_post’, postid: 1, atributos: {id: 1, autor: ‘toni’,},};Para la segunda opción, debemos agregar explícitamente el atributo de identificación sobre el tipo parcial Definimos:
Escriba updatePostAction = {type: ‘update_post’; Atributos: parcialmente & {id: postid}; }; que, nuevamente, nos da el resultado esperado: Const Workingction: UpdatePostction = {type: ‘update_post’, atributos: {id: 1, autor: ‘toni’,},}; Const fallingction: updatePostction = {type: ‘update_post’, atributes: {autor: ‘toni’,},}; Tipos de sindicatos En la sección anterior, ya hemos visto cómo introducir una de las dos acciones que tiene nuestra tienda. Hagamos lo mismo con la segunda acción. Sabiendo que recibeNewPost se ve así: function ReceptiveWpost (post: post): Any {return {type: ‘recibe_new_post’, post,}; } Su tipo se puede definir de la siguiente manera: type recibeVNewPostction = {type: ‘recibe_new_post’; Publicación: Post; }; Fácil, ¿verdad? Ahora echemos un vistazo a nuestro reductor: tome un estado y una acción (cuyo tipo aún no conocemos) y produce un nuevo estado: Reductor de funciones (Estado: Estado, Acción: Any): Estado {…} La tienda Nuestro tiene dos tipos diferentes de acciones: UpdatePostction y ReceptNeWpostction. Entonces, ¿cuál es el tipo de argumento de acción? Uno u otro, ¿verdad? Cuando una variable puede aceptar más de un tipo A, B, C, etc., su tipo es una unión de tipos. Quiero decir, su tipo puede ser A o B o C, y así sucesivamente. Un tipo de unión es un tipo cuyos valores pueden ser de cualquiera de los tipos especificados en esa unión. Así es como nuestro tipo de acción se puede definir como un tipo de unión: type Action = UpdatePostction | ReceptNewpostction;
El fragmento anterior simplemente establece que una acción puede ser una instancia del tipo UpdatePostaction o una instancia del tipo de poste de poste.Si ahora usamos la acción en nuestro Reductor: Funcion Reducer (Estado: Estado, Acción: Acción): Estados {…} Podemos ver cómo funciona esta nueva versión de nuestro código sin problemas.Como los tipos de uniones eliminan los casos predeterminados “espere un segundo”, se podría decir: “¡El enlace anterior no funciona bien, el compilador desencadena un error!”De hecho, de acuerdo con TypeScript, nuestro reductor contiene código inaccesible: Reductor de funciones (Estado: Estado, Acción: Acción): Estado {// … Switch (Action.Type) {case ‘recibe_new_post’: // … casas ‘ Update_post ‘: // …} Return States;
// ¡error! Código inalcanzable} ¿Quédese qué? Permítanme explicar qué está sucediendo aquí … El tipo de sindicato de acción que creé es en realidad un tipo de unión discriminada. Un tipo de unión discriminada es un tipo de unión en el que todos sus tipos tienen un atributo común cuyo valor puede usarse para discriminar por un tipo de otro. En nuestro caso, los dos tipos de acción tienen un atributo de tipo cuyos valores son Recibe_New_Post para ReceptIVNewPostction y Update_Post para UpdatePosTulation. Debido a que sabemos que una acción es necesariamente una instancia de una acción u otra, las dos ramas de nuestro conmutador cubren todas las posibilidades: o acción. El type es recibe_new_post o es update_post. Por lo tanto, el retorno final es redundante y será inaccesible. Supongamos entonces que eliminamos ese regreso para remediar este error. Gané algo, además de la eliminación del código innecesario. La respuesta es sí. Si ahora agregamos un nuevo tipo de acción en nuestro código: type Action = | UpdatePostAction | ReceptNewpostction | Newfeaturection; Type newfeatUrection = {type: ‘new_feature’; // …}; De repente, la instrucción Switch en nuestro reductor no cubrirá todos los escenarios posibles: Reductor de funciones (Estado: Estado, Acción: Action): State {// … Switch (Action.Type) {case ‘recibe_new_post’ / / .. .. . Houses ‘update_post’
: // … // Houses New_Feature Falta …} // El retorno Undefinado es predeterminado} Esto significa que el reductor podría devolver implícitamente un valor subfinado si lo invocamos usando un tipo New_Fature, y esto es algo que no Haga coincidir la firma de la función. Debido a este desajuste, TypeScript se queja y anuncia que falta una nueva rama para hacer frente a este nuevo tipo de acción. Funciones polimórficas con tipos de devolución variable Si ha llegado aquí, felicidades: ha aprendido todo lo que necesita para mejorar el código fuente de JavaScript usando TypeScript. Y, como recompensa, voy a compartir un “problema” que conocí hace unos días y su solución. ¿Por qué? Porque TypeScript es un mundo complejo y fascinante y quiero mostrarle en qué medida es cierto. Al comienzo de esta aventura, vi que uno de los selectores que tenemos es GetPostsInday y cómo su tipo de devolución es una lista de publicaciones: function getPostsInday (estado: estado, día: día): postid [] {return state.days [ día] ?? []; } Incluso si el nombre sugiere que podrían devolver una lista de publicaciones. ¿Por qué usé un nombre tan engañoso, te preguntas? Bueno, imagine el siguiente escenario: suponga que desea que esta característica pueda devolver una lista de ID de publicación o devolver una lista de publicaciones reales, dependiendo del valor de uno de sus argumentos. Algo como: const IDS: postid [] = getPostsInday (‘2020-10-01’, ‘id’); Const Publics: Post [] = getPostsInday (‘2020-10-01’, ‘all’);

¿Podemos hacer esto en TypeScript? ¡Por supuesto lo hacemos! De lo contrario, ¿por qué traería esto en discusión? Todo lo que tenemos que hacer es definir una función polimórfica cuyo resultado depende de los parámetros de entrada. Entonces, la idea es que queremos dos versiones diferentes de la misma función. Debería devolver una lista de PostIds si uno de los atributos es la ID de cadena. El otro debe devolver una lista de publicaciones si el mismo atributo es cadena todo. Creemos ambos: function getPostsInday (estado: estado, día, modo: ‘id’): postid [] {// …} function getPostsInday (estado: estado, día, modo: ‘all’): post [] {// …} fácil, ¿verdad? ¡EQUIVOCADO! Esto no funciona. Según TypeScript, tenemos una “implementación de la función duplicada”. Bien, entonces intentemos algo diferente. Combinemos las dos definiciones anteriores en una función: función getPostsInday (estado: estado, día: día, modo: ‘id’ | ‘all’ = ‘id’): postid [] | Post [] {if (‘id’ === MODE) {return state.days [día] ?? []; } devolver []; } ¿Se comporta como queremos? Me temo que no … aquí es lo que nos dice esta firma de la función: “GetPostSinday es una función que toma dos argumentos, un estado y una moda cuyos valores pueden ser ID o todo; Su tipo de devolución será una lista de postids o una lista de publicaciones “. En otras palabras, la definición anterior de la función no es específica para ninguna parte de que exista una relación entre el valor dado al argumento de modo y el tipo de retorno de la función. Y entonces usted bacalao así: const estados: state = {publicaciones: {}, días: {}; Const: postid [] = getPostsInday (estado, 2020-10-01 ‘,’ id ‘); Const Posts: Post [] = getPostsInday (estado, ‘
2020-10-01 ‘,’ all ‘); Es válido y no se comporta como queramos. Ok, el último intento. ¿Qué sucede si mezclamos nuestra intuición inicial, en la que describimos firmas de funciones concretas, con una sola implementación válida? Función getPostSinday (estado: estado, día: día, modo: ‘id’): postid []; Función getPostSinday (estado: estado, día: día, modo: ‘all’): post []; Function getPostSinday (estado: estado, día: día, modo: ‘id’ | ‘all’): postid [] | Post [] {const postids = state.days [día] ?? []; if (‘id’ === MODE) {return PostIds; } return postids .map ((pid) => getPost (estado, pid)) .filter ((p): p es post => !! p); } El fragmento anterior tiene una implementación válida de la función de funcionamiento, pero define dos firmas de función adicionales que vinculan valores concretos en modo con el tipo de retorno de la función. Usando este enfoque, este código es válido: const IDS: postid [] = getPostsInday (estado, 2020-10-01 ‘,’ id ‘); Const Posts: Post [] = getPostsInday (estado, 2020-10-01 ‘,’ all ‘); y esto no: const IDS: postid [] = getPostsInday (estado, 2020-10-01 ‘,’ all ‘); Const Posts: Post [] = getPostsInday (estado, 2020-10-01 ‘,’ id ‘);
TypeScript avanzó con un ejemplo real (Parte 2)
Tags TypeScript avanzó con un ejemplo real (Parte 2)
homefinance blog