Genéricos avanzados en TypeScript. Informe Yandex

Los genéricos, o tipos parametrizados, le permiten escribir funciones e interfaces más flexibles. Para ir más allá de la parametrización con un solo tipo, solo necesita comprender algunos principios generales de los genéricos, y TypeScript se abrirá ante usted como una caja de secretos. AlexandrNikolaichev explicó cómo no tener miedo de anidar genéricos entre sí y usar la inferencia de tipo automática en sus proyectos.



- Hola a todos, mi nombre es Alexander Nikolaichev. Trabajo en Yandex.Cloud como desarrollador front-end, trabajando en la infraestructura interna de Yandex. Hoy les contaré algo muy útil, sin el cual es difícil imaginar una aplicación moderna, especialmente a gran escala. Este es TypeScript, mecanografía, un tema más limitado: los genéricos y por qué son necesarios.



Primero, respondamos la pregunta de por qué TypeScript y qué tiene que ver la infraestructura con él. Nuestra principal propiedad de la infraestructura es su confiabilidad. ¿Cómo se puede garantizar esto? En primer lugar, puede probar.





Disponemos de pruebas unitarias y de integración. Las pruebas son una buena práctica estándar.



También debe utilizar la revisión de código. Además, colección de errores. Sin embargo, si ocurre un error, entonces un mecanismo especial lo envía y podemos arreglar algo rápidamente.



Qué bonito sería no cometer ningún error. Para esto, está la escritura, que no nos permitirá obtener ningún error en tiempo de ejecución. Yandex utiliza el TypeScript estándar de la industria. Y dado que las aplicaciones son grandes y complejas, obtendremos esta fórmula: si tenemos una interfaz, escritura e incluso abstracciones complejas, definitivamente llegaremos a los genéricos de TypeScript. No puedes prescindir de ellos.



Sintaxis



Para llevar a cabo un programa educativo básico, primero veamos los conceptos básicos de la sintaxis.



Un genérico en TypeScript es un tipo que depende de otro tipo.



Tenemos un tipo simple, Page. Lo parametrizamos con un determinado parámetro <T>, se escribe entre paréntesis angulares. Y vemos que hay algunas cadenas, números, pero <T> es variable.



Además de las interfaces y los tipos, podemos aplicar la misma sintaxis a las funciones. Es decir, se reenvía el mismo parámetro <T> al argumento de la función, y en la respuesta reutilizaremos la misma interfaz, también la pasaremos allí.



Nuestra llamada genérica también se escribe entre paréntesis angulares con el tipo deseado, al igual que cuando se inicializó.



Existe una sintaxis similar para las clases. Lanzamos el parámetro a campos privados y tenemos una especie de captador. Pero no escribimos el tipo allí. ¿Por qué? Porque TypeScript puede inferir el tipo. Esta es una característica suya muy útil y la aplicaremos.



Veamos qué sucede al usar esta clase. Creamos una instancia y, en lugar de nuestro parámetro <T>, pasamos uno de los elementos de enumeración. Creamos una enumeración: ruso, inglés. TypeScript entiende que hemos pasado un elemento de la enumeración e infiere el tipo lang.



Pero veamos cómo funciona la inferencia de tipos. Si en lugar de elementos de enumeración pasamos una constante de esta enumeración, entonces TypeScript entiende que esta no es la enumeración completa, no todos sus elementos. Y ya habrá un valor específico del tipo, es decir, lang en, inglés.



Si pasamos algo más, por ejemplo, una cadena, entonces parecería que tiene el mismo significado que la enumeración. Pero esto ya es una cadena, otro tipo en TypeScript, y lo obtendremos. Y si pasamos una cadena como una constante, en lugar de una cadena habrá una constante, una cadena literal, no todas son cadenas. En nuestro caso, habrá una cadena específica en.



Ahora veamos cómo podemos expandir esto.



Teníamos un parámetro. Nada nos impide utilizar múltiples parámetros. Todos ellos están escritos separados por comas. En los mismos paréntesis angulares, y los aplicamos en orden, del primero al tercero. Sustituimos los valores deseados cuando se nos llama.



Digamos una concatenación de literales numéricos, algún tipo estándar, una concatenación de literales de cadena. Todos están simplemente escritos en orden.



Veamos cómo sucede esto en las funciones. Creamos una función aleatoria. Da aleatoriamente el primer argumento o el segundo.



El primer argumento es de tipo A, el segundo es de tipo B. En consecuencia, se devuelve su unión: esto o esto. En primer lugar, podemos escribir explícitamente la función. Indicamos que A es una cadena, B es un número. TypeScript observará lo que hemos especificado explícitamente e inferirá el tipo.



Pero también podemos usar la inferencia de tipos. Lo principal es saber que no es solo el tipo que se infiere, sino el tipo más pequeño posible para el argumento.



Supongamos que pasamos un argumento, un literal de cadena, y debe corresponder al tipo A, y el segundo argumento, uno, al tipo B. El mínimo posible para un literal de cadena y uno es el literal A y el mismo. TypeScript nos enviará esto. Resulta tal reducción de tipos.



Antes de pasar a los siguientes ejemplos, veremos cómo los tipos se relacionan generalmente entre sí, cómo usar estas relaciones, cómo poner orden en el caos de todos los tipos.



Relación de tipos



Los tipos pueden considerarse convencionalmente como una especie de conjunto. Veamos el diagrama, que muestra una parte de todo el conjunto de tipos.



Vemos que los tipos en él están conectados por algún tipo de relación. Pero cuales? Se trata de relaciones de ordenación parcial, lo que significa que un tipo siempre se especifica con su supertipo, es decir, un tipo "por encima" de él, que cubre todos los valores posibles.



Si va en la dirección opuesta, entonces cada tipo puede tener un subtipo, "menos".



¿Cuáles son los supertipos de una cadena? Cualquier combinación que incluya una cadena. Una cadena con un número, una cadena con una matriz de números, lo que sea. Los subtipos son todos literales de cadena: a, b, c, o ac, o ab.



Pero es importante comprender que el orden no es lineal. Es decir, no se pueden comparar todos los tipos. Esto es lógico, y esto es lo que conduce a errores de desajuste de tipos. Es decir, una cadena no se puede comparar simplemente con un número.



Y en este orden hay un tipo, por así decirlo, el más alto: desconocido. Y lo más bajo, análogo del conjunto vacío, nunca lo es. Nunca es un subtipo de ningún tipo. Y desconocido es un supertipo de cualquier tipo.



Y, por supuesto, hay una excepción: cualquiera. Este es un tipo especial, ignora este orden por completo y se usa si estamos migrando desde JavaScript para no preocuparnos por los tipos. No se recomienda utilizar ninguno desde cero. Vale la pena hacer esto si realmente no nos importa la posición del tipo en ese orden.



Veamos qué nos aporta el conocimiento de esta orden.



Podemos restringir los parámetros a sus supertipos. La palabra clave se extiende. Definiremos un tipo, genérico, que tendrá un solo parámetro. Pero diremos que solo puede ser un subtipo de la cadena o la cadena en sí. No podremos transferir números, esto provocará un error de tipo. Si escribimos explícitamente la función, entonces en los parámetros podemos especificar solo los subtipos de la cadena o la cadena: manzana y naranja. Ambas cadenas son una concatenación de cadenas literales. Pasó la verificación.





También podemos inferir tipos automáticamente nosotros mismos en función de los argumentos. Si pasamos un literal de cadena, entonces también es una cadena. El cheque funcionó.



Veamos cómo ampliar estas restricciones.



Nos limitamos a una sola línea. Pero una cadena es un tipo demasiado simple. Me gustaría trabajar con claves de objeto. Para trabajar con ellos, primero comprendemos cómo se organizan las claves de objeto y sus tipos.



Tenemos cierto objeto. Tiene algún tipo de campos: cadenas, números, valores booleanos y claves por nombre. Para obtener las claves, usamos la palabra clave keyof. Obtenemos la unión de todos los nombres clave.



Si queremos obtener los valores, podemos hacerlo mediante la sintaxis de los corchetes. Esto es similar a la sintaxis de JS. Solo devuelve tipos. Si pasamos todo el subconjunto de claves, obtenemos la unión de todos los valores de este objeto en general.



Si queremos obtener una parte, entonces podemos especificar eso, no todas las claves, sino algún subconjunto. Esperamos recibir solo aquellos campos que corresponden a las claves especificadas. Si reducimos todo a un solo caso, este es un campo y una clave da un valor. De esta forma puede obtener el campo correspondiente.



Veamos cómo usar las claves de objeto.



Es importante comprender que puede haber cualquier tipo válido después de la palabra clave extiende. Incluyendo formados a partir de otros genéricos o usando palabras clave.



Veamos cómo funciona esto con keyof. Hemos definido el tipo CustomPick. De hecho, esta es una copia casi completa del tipo de biblioteca Pick de TypeScript. ¿Qué está haciendo?



Tiene dos parámetros. El segundo no es solo un parámetro. Deben ser las claves del primero. Vemos que lo tenemos expandiendo keyof desde <T>. Por lo tanto, debe ser algún subconjunto de claves.



A continuación, para cada clave K de este subconjunto, corremos alrededor del objeto, ponemos el mismo valor y eliminamos especialmente la opcional, menos el signo de interrogación con la sintaxis. Es decir, todos los campos serán obligatorios.



Miramos la aplicación. Tenemos un objeto, en él los nombres de los campos. Podemos tomar solo un subconjunto de ellos: a, bo c, o todos a la vez. Tomamos a o c. Solo se muestran los valores correspondientes, pero vemos que el campo a se ha vuelto obligatorio, porque, en términos relativos, eliminamos el signo de interrogación. Definimos este tipo, lo usamos. Nadie nos molesta en tomar este genérico y meterlo en otro genérico.



¿Como sucedió esto? Hemos definido otro tipo, Custom. El segundo parámetro no expande keyof, sino el resultado de aplicar el genérico, que hemos mostrado a la derecha. ¿Cómo funciona, qué le estamos transfiriendo?



Pasamos cualquier objeto y todas sus claves a este genérico. Esto significa que la salida será una copia del objeto con todos los campos obligatorios. Esta cadena de anidar un genérico en otro genérico y así sucesivamente puede continuar indefinidamente, dependiendo de las tareas, y estructurar el código. Introduzca construcciones reutilizables en genéricos, etc.



Los argumentos especificados no tienen que estar en orden. Algo así como el parámetro P expande las teclas T en el CustomPick genérico. Pero nadie nos molestó en indicarlo como primer parámetro y T como segundo. TypeScript no pasa secuencialmente por los parámetros. Mira todos los parámetros que hemos especificado. Luego, resuelve cierto sistema de ecuaciones, y si encuentra una solución a los tipos que satisfacen este sistema, entonces la verificación de tipo pasa.



En este sentido, puede derivar un genérico tan divertido, en el que los parámetros expanden las claves de los demás: a - estas son las claves b, b - las claves a. Parecería, ¿cómo puede ser esto, las llaves de las llaves? Pero sabemos que las cadenas de TypeScript son en realidad cadenas de JavaScript y las cadenas de JavaScript tienen sus propios métodos. En consecuencia, cualquier nombre de método de cadena servirá. Porque el nombre de un método de cadena también es una cadena. Y de ahí tiene su nombre.



En consecuencia, podemos obtener tal restricción, y el sistema de ecuaciones se resolverá si indicamos los tipos requeridos.



Veamos cómo se puede utilizar esto en la realidad. Lo usamos para la API. Hay un sitio donde se implementan las aplicaciones de Yandex. Queremos mostrar el proyecto y el servicio que le corresponde.



En el ejemplo, tomé un proyecto para ejecutar máquinas virtuales qyp para desarrolladores. Sabemos que tenemos la estructura de este objeto en el backend, lo tomamos de la base. Pero además del proyecto, hay otros objetos: borradores, recursos. Y todos tienen sus propias estructuras.



Además, queremos solicitar no todo el objeto, sino un par de campos: el nombre y el nombre del servicio. Existe tal oportunidad, el backend le permite pasar caminos y recibir una estructura incompleta. DeepPartial se describe aquí. Aprenderemos a diseñarlo un poco más adelante. Pero esto significa que no se transfiere todo el objeto, sino parte de él.



Queremos escribir alguna función que solicite estos objetos. Escribamos en JS. Pero si miras de cerca, puedes ver errores tipográficos. En el tipo de "Projeact", en las rutas también hay un error tipográfico en el servicio. No es bueno, el error estará en tiempo de ejecución.



La variante TS no parece ser muy diferente aparte de las rutas. Pero mostraremos que, de hecho, no puede haber otros valores en el campo Tipo además de los que tenemos en el backend.



El campo de rutas tiene una sintaxis especial que simplemente no nos permite seleccionar otros campos faltantes. Usamos una función en la que simplemente enumeramos los niveles de anidación que necesitamos y obtenemos un objeto. De hecho, obtener rutas de esta función es una preocupación de nuestra implementación. Aquí no hay ningún secreto, ella usa un proxy. Esto no es tan importante para nosotros.



Veamos cómo obtener la función.





Tenemos una función, su uso. Existe esta estructura. Primero, queremos obtener todos los nombres. Escribimos un tipo donde el nombre coincide con la estructura.



Digamos que para un proyecto describimos su tipo en alguna parte. En nuestro proyecto, generamos taipings a partir de archivos protobuf que están disponibles en el repositorio general. A continuación, vemos que tenemos todos los tipos utilizados: Proyecto, Borrador, Recurso.



Veamos la implementación. Veámoslo en orden.



Hay una función. Primero, veamos cómo está parametrizado. Solo por estos nombres descritos anteriormente. Veamos qué devuelve. Devuelve valores. ¿Por qué esto es tan? Hemos utilizado la sintaxis de corchetes. Pero como estamos pasando una cadena al tipo, la concatenación de cadenas literales cuando se usa es siempre una cadena. No es posible componer una cadena que sea un proyecto y un recurso al mismo tiempo. Ella es siempre una, y el significado también es el mismo.



Envuelva todo en DeepPartial. Tipo opcional, estructura opcional. Lo más interesante son los parámetros. Les preguntamos con la ayuda de otro genérico.



El tipo con el que se parametrizan los parámetros genéricos también coincide con la restricción de la función. Solo puede aceptar el tipo de nombre: Proyecto, Recurso, Borrador. La identificación es, por supuesto, una cadena, no nos interesa. Aquí está el tipo que indicamos, uno de tres. Me pregunto cómo funciona la función de ruta. Este es otro genérico, ¿por qué no lo reutilizamos? De hecho, todo lo que hace es simplemente crear una función que devuelve una matriz de cualquiera, porque nuestro objeto puede tener campos de cualquier tipo, no sabemos cuáles. En esta implementación, obtenemos control sobre los tipos.



Si alguien lo encontró simple, pasemos a las estructuras de control.



Construcciones de control



Consideraremos solo dos construcciones, pero serán suficientes para cubrir casi todas las tareas que necesitamos.



¿Qué son los tipos condicionales? Son muy similares a los ternarks en JavaScript, solo para tipos. Tenemos una condición de que el tipo a es un subtipo de b. Si es así, devuelva c. Si no es así, regrese d. Es decir, esto es normal si, solo para tipos.



Vamos a ver cómo funciona. Definiremos un tipo CustomExclude, que esencialmente copia la biblioteca Exclude. Simplemente descarta los elementos que necesitamos de la unión de tipos. Si a es un subtipo de b, devuelve vacío; de lo contrario, devuelve a. Esto es extraño cuando miras por qué funciona con uniones.



Una ley especial es útil, que dice: si hay una unión y verificamos las condiciones usando extend, luego verificamos cada elemento por separado y luego los combinamos nuevamente. Esta es una ley tan transitiva, solo para tipos condicionales.



Cuando usamos CustomExclude, miramos cada elemento de observación por turno. a expande a, a es un subtipo, pero devuelve vacío; b es un subtipo de a? No - regreso b. c no es un subtipo de a tampoco, devuelve c. Luego combinamos lo que queda, todos los signos más, obtenemos by c. Tiramos un y obtuvimos lo que queríamos.



Se puede utilizar la misma técnica para obtener todas las claves de una tupla. Sabemos que una tupla es la misma matriz. Es decir, tiene métodos JS, pero no necesitamos esto, solo necesitamos índices. En consecuencia, simplemente descartamos los nombres de todos los métodos de todas las claves de la tupla y obtenemos solo los índices.



¿Cómo definimos nuestro tipo DeepPartial mencionado anteriormente? Aquí es donde se usa la recursividad por primera vez. Pasamos por todas las claves del objeto y miramos. ¿Es el valor un objeto? Si es así, aplíquelo de forma recursiva. Si no es así, y se trata de una cadena o un número, déjelo y haga que todos los campos sean opcionales. Sigue siendo de tipo parcial.



Esta llamada recursiva y los tipos condicionales en realidad completan TypeScript Turing. Pero no se apresure a alegrarse de esto. Te cabrea si intentas hacer algo como esto, una abstracción con mucha recursividad.



TypeScript supervisa esto y arroja un error incluso en el nivel de su compilador. Ni siquiera esperará hasta que se cuente algo allí. Y para casos tan simples, donde solo tenemos una llamada, la recursividad es bastante adecuada.



Vamos a ver cómo funciona. Queremos resolver el problema de parchear el campo del objeto. Usamos una nube virtual para planificar el lanzamiento de aplicaciones y necesitamos recursos.



Digamos que tomamos recursos de CPU, núcleos. Todo el mundo necesita granos. He simplificado el ejemplo y solo hay recursos, solo kernels, y son números.



Queremos hacer una función que los parchee, parchee valores. Agregue granos o reste. En el mismo JavaScript, como habrás adivinado, hay errores tipográficos. Aquí agregamos un número a una cadena, no muy bien.



Casi nada ha cambiado en TypeScript, pero de hecho, este control a nivel IDE te dirá que no puedes pasar nada más que esta cadena o un número específico.



Veamos cómo lograrlo. Necesitamos obtener dicha función y sabemos que tenemos un objeto de este tipo. Debe comprender que solo estamos parcheando el número y los campos. Es decir, necesita obtener el nombre de solo aquellos campos donde hay números. Solo tenemos un campo y es un número.



Veamos cómo se implementa esto en TypeScript.



Hemos definido una función. Tiene solo tres argumentos, el objeto que estamos parcheando y el nombre del campo. Pero este no es solo un nombre de campo. Solo puede ser el nombre de campos numéricos. Ahora descubriremos cómo se hace esto. Y el parche en sí, que es una función pura.



Existe una cierta función impersonal, un parche. No nos interesa su implementación, sino cómo obtener un tipo tan interesante, para obtener las claves no solo numéricas, sino de cualquier campo por condición. Tenemos números aquí.



Analicemos en orden cómo sucede esto.



Pasamos por todas las claves del objeto pasado, luego hacemos este procedimiento. Veamos que el campo del objeto es un subtipo del deseado, es decir, un campo numérico. Si es así, entonces es importante que escribamos no el valor del campo, sino el nombre del campo, de lo contrario, en general, nada, nunca.



Pero luego resultó un objeto tan extraño. Todos los campos numéricos comenzaron a tener sus nombres como valores y todos los campos no numéricos quedaron vacíos. Luego tomamos todos los valores de este extraño objeto.



Pero dado que todos los valores contienen vacío, y el vacío se colapsa cuando se combinan, solo quedan los campos que corresponden a los numéricos. Es decir, solo obtuvimos los campos obligatorios.



El ejemplo muestra: hay un objeto simple, el campo es uno. ¿Es este un número? Si. El campo es un número, ¿es un número? Si. La última línea no es un número. Obtenemos solo los campos numéricos necesarios.



Con esto resuelto. Dejé el más difícil para el final. Esta es la inferencia de tipo: inferir. Captura de un tipo en una construcción condicional.





Es inseparable del tema anterior, porque solo funciona con una construcción condicional.



Cómo se ve? Digamos que queremos conocer los elementos de una matriz. Llegó un cierto tipo de matriz, nos gustaría conocer un elemento específico. Miramos: recibimos algún tipo de matriz. Es un subtipo de la matriz de la variable x. En caso afirmativo, devuelva este elemento de matriz x. Si no, devuelve el vacío.



En esta condición, la segunda rama nunca se ejecutará, porque parametrizamos el tipo con cualquier matriz. Por supuesto, será una matriz de algo, porque una matriz de cualquiera no puede dejar de tener elementos.



Si pasamos una matriz de cadenas, se espera que se nos devuelva una cadena. Y es importante entender que no es solo un tipo que se define aquí. Es visualmente claro a partir de la matriz de cadenas: hay cadenas. Pero con una tupla, no todo es tan simple. Es importante para nosotros saber que se está determinando el supertipo más pequeño posible. Está claro que todas las matrices son, por así decirlo, subtipos de una matriz con cualquiera o con desconocido. Este conocimiento no nos aporta nada. Para nosotros es importante saber lo mínimo posible.



Supongamos que estamos pasando en una tupla. De hecho, las tuplas también son matrices, pero ¿cómo sabemos qué elementos tiene esta matriz? Si hay una tupla de cadena de un número, entonces en realidad es una matriz. Pero el elemento debe ser del mismo tipo. Y si hay tanto una cadena como un número, entonces habrá una unión.



TypeScript generará esto y obtendremos exactamente la concatenación de una cadena y un número para tal ejemplo.



Puede utilizar no solo la captura en un lugar, sino también tantas variables como desee. Digamos que definimos un tipo que simplemente intercambia los elementos de la tupla: el primero con el segundo. Cogemos el primer elemento, el segundo y los intercambiamos.



Pero, de hecho, no se recomienda coquetear demasiado con él. Por lo general, para el 90% de las tareas, solo un tipo de captura es suficiente.





Veamos un ejemplo. Objetivo: es necesario mostrar, según el estado de la solicitud, una buena o una mala opción. Aquí hay capturas de pantalla de nuestro servicio de implementación de aplicaciones. Una entidad, ReplicaSet. Si la solicitud del backend arrojó un error, debe procesarla. Al mismo tiempo, existe una API para el backend. Veamos qué tiene que ver Infer con eso.



Sabemos que estamos usando, en primer lugar, redux y, en segundo lugar, redux thunk. Y necesitamos transformar el procesador de la biblioteca para poder hacer esto. Tenemos un mal camino y uno bueno.



Y sabemos que una buena forma de introducir extraReducers en el kit de herramientas de redux se ve así. Sabemos que existe PayLoad y queremos extraer los tipos personalizados que nos llegaron del backend, pero no solo, sino también información sobre una solicitud buena o mala: hay un error o no. Necesitamos un genérico para esta salida.



No estoy haciendo una comparación sobre JavaScript porque no tiene sentido. En JavaScript, en principio, no puede controlar los tipos de ninguna manera y depende solo de la memoria. No hay una mala opción aquí, porque simplemente no la hay.



Sabemos que queremos este tipo. Pero no solo tenemos una acción. Necesitamos llamar al despacho con esta acción. Y necesitamos esta vista, donde necesitamos mostrar un error por la clave de solicitud. Es decir, necesita mezclar dicha funcionalidad adicional además de redux thunk usando el método withRequestKey.



Tenemos este método, por supuesto, pero también tenemos el método API original, getReplicaSet. Está escrito en alguna parte y necesitamos anular el procesador redux usando algún tipo de adaptador. Veamos cómo hacerlo.



Necesitamos obtener una función como esta. Es un procesador con mucha funcionalidad adicional. Suena aterrador, pero no te alarmes, ahora lo desmontaremos en las estanterías para que quede despejado.



Hay un adaptador que amplía el tipo de biblioteca original. Simplemente mezclamos un método withRequestKey adicional y una llamada personalizada a este tipo de biblioteca. Veamos cuál es la característica principal del genérico, qué parámetros se utilizan.



El primero es solo nuestra API, un objeto con métodos. Podemos hacer getReplicaSet, obtener proyectos, recursos, no importa. Estamos usando un método específico en el método actual, y el segundo parámetro es solo el nombre del método. A continuación, usamos los parámetros de la función que estamos solicitando, usamos el tipo de biblioteca Parameters, este es un tipo TypeScript. Y de manera similar, usamos el tipo de biblioteca ReturnType para la respuesta de backend. Esto es por lo que devolvió la función.



Luego, simplemente pasamos nuestra salida personalizada al tipo AsyncThunk que nos proporcionó la biblioteca. Pero, ¿cuál es esta conclusión? Este es otro medicamento genérico. De hecho, parece simple. Guardamos no solo la respuesta del servidor, sino también nuestros parámetros, lo que hemos pasado. Solo para realizar un seguimiento de ellos en Reducer. A continuación, miramos conRequestKey. Nuestro método solo agrega una clave. ¿Qué devuelve? El mismo adaptador porque podemos reutilizarlo. No tenemos que escribir conRequestKey en absoluto. Esta es solo una funcionalidad adicional. Envuelve y nos devuelve de forma recursiva el mismo adaptador, y pasamos lo mismo allí.



Finalmente, veamos cómo enviar a Reducer lo que este procesador nos devolvió.





Tenemos este adaptador. Lo principal es recordar que existen cuatro parámetros: API, método API, parámetros (entrada) y salida. Necesitamos encontrar una salida. Pero recordamos que tenemos una salida personalizada: tanto la respuesta del servidor como el parámetro de solicitud.



¿Cómo puedo hacer esto con Infer? Vemos que este adaptador se suministra a la entrada, pero generalmente es any: any, any, any, any. Tenemos que devolver este tipo, se ve así, la respuesta del servidor y los parámetros de la solicitud. Y estamos viendo dónde debería estar la entrada. En el tercero. Aquí es donde colocamos nuestra captura de tipo. Conseguimos la entrada. Asimismo, la salida está en cuarto lugar.



TypeScript se basa en la escritura estructurada. Desarma esta estructura y entiende que la entrada está aquí, en el tercer lugar, y la salida está en el cuarto. Y devolvemos los tipos que queremos.



Así que logramos la inferencia de tipos, ya tenemos acceso a ellos en el propio Reducer. Es básicamente imposible hacer esto en JavaScript.



All Articles