Hagamos un Algoritmo de Inferencia Genérica

Rate this content
Bookmark

¿Cómo funciona la inferencia genérica en TypeScript? Para la mayoría de las personas, esto puede parecer una caja negra. El líder de desarrollo de TypeScript, Ryan Cavanaugh, nos guía desde un simple algoritmo de inferencia genérica de un solo paso hasta un modelo simplificado de cómo realmente funciona la inferencia genérica en TypeScript, con un enfoque en ejemplos motivadores.

Ryan Cavanaugh
Ryan Cavanaugh
25 min
21 Sep, 2023

Comments

Sign in or register to post your comment.

Video Summary and Transcription

Hola, bienvenidos a Hagamos un Algoritmo de Inferencia. Hoy, les daré una visión general de alto nivel de cómo funciona el proceso de inferencia genérica de TypeScript. Exploraremos diferentes posibilidades como 'any', 'unknown' o 'number'. El algoritmo para inferir argumentos de tipo recopila candidatos y elige el primero, asegurando la mejor respuesta correcta. El concepto del mejor supertipo común se utiliza para determinar el mejor candidato. La sensibilidad al contexto se aborda en el algoritmo, permitiendo un comportamiento óptimo.

Available in English

1. Introducción a la inferencia genérica de TypeScript

Short description:

Hola, bienvenidos a Vamos a Crear un Algoritmo de Inferencia. Soy Ryan Kavanaugh, el líder de desarrollo para el equipo del compilador de TypeScript en Microsoft. Hoy, les daré una visión general de alto nivel de cómo funciona el proceso de inferencia genérica de TypeScript. Discutiremos ejemplos, posibles opciones y compensaciones. Comencemos con un ejemplo: F, una función con un parámetro de tipo, un parámetro regular y un tipo de retorno. TypeScript infiere el argumento de tipo basado en el valor proporcionado. Exploraremos diferentes posibilidades como 'any', 'unknown' o 'number'.

Hola, bienvenidos a Vamos a Crear un Algoritmo de Inferencia. Soy Ryan Kavanaugh. Soy el líder de desarrollo para el equipo del compilador de TypeScript en Microsoft. Mis objetivos hoy son mostrarles una visión general de alto nivel de cómo funciona el proceso de inferencia genérica de TypeScript.

La gente a menudo me pregunta cómo funciona esto y no puedo darles una explicación completa en 20 minutos, pero podemos repasar muchos de los conceptos de alto nivel y explicar cuáles son los objetivos generales. Esta va a ser una charla basada en ejemplos porque nuestro lenguaje está impulsado por ejemplos motivadores que realmente muestran por qué las cosas funcionan de la manera en que lo hacen, y vamos a discutir otras posibles opciones de cómo podrían funcionar las cosas y discutir algunas de las compensaciones que se toman al tomar diferentes decisiones en el camino.

Así que comencemos con un ejemplo. Tengo F aquí. Tiene un parámetro de tipo, T, tiene un parámetro regular, x, de tipo T, y devuelve un T. Cuando TypeScript ve una llamada a una función genérica, actúa como si hubieras escrito un argumento de tipo aquí. Puedes escribir argumentos de tipo tú mismo, pero no tienes que hacerlo. Y si no lo haces, intentaremos averiguar qué quieres que vaya aquí. En este caso, vamos a pasar el valor 42 para x, y la pregunta que necesitamos responder es, como, rellenar el espacio en blanco. ¿Qué va allí? Si hubiéramos obligado al usuario a escribir un argumento de tipo en esa posición, ¿qué habrían escrito? Podemos empezar a hacer algunas suposiciones sobre lo que podríamos querer hacer.

Una cosa que podrías decir es, es cualquier cosa. Si no nos dices, simplemente vamos a asumir cualquier cosa, y luego dejar que las fichas caigan donde puedan, ¿verdad? Eso no sería muy bueno. Creo que a la gente no le gustaría eso. Podríamos decir desconocido. En este ejemplo, desconocido es una apuesta segura, ¿verdad? 42 es un desconocido válido. Lo que sea que f devuelva seguramente será un desconocido. Así que desconocido es una inferencia correcta, y podrías hacer eso, y podrías hacer un argumento teórico para desconocido. Podríamos decir, bueno, 42 es un primitivo. Los primitivos son número y cadena y booleano. Así que tal vez quisiste decir toda la familia de primitivos cuando invocaste f aquí, y tal vez eso es lo que quieres que suceda. O podríamos decir, no, número. 42 es un número. Probablemente eso es lo que quieres para t. También podríamos hablar sobre el tipo literal 42. Voy a fingir que los tipos literales no existen para el propósito de esta charla, porque complica mucho las cosas. Así que un número es lo más bajo que va ahora.

2. Algoritmo para Especificar la Inferencia Genérica

Short description:

Creemos que el número es la respuesta correcta aquí. Parece que si llamaste a f de t y le diste un número, el valor más específico que podemos elegir es el correcto. Entonces, ¿cuál es el algoritmo que escribiríamos si tuviéramos que especificar cómo funciona eso?

Creemos que el número es la respuesta correcta aquí. Parece que si llamaste a f de t y le diste un número, el valor más específico que podemos elegir es el correcto. Entonces, ¿cuál es el algoritmo que escribiríamos si tuviéramos que especificar cómo funciona eso? Bueno, diríamos que vamos a buscar t y encontrar un lugar donde ocurra en la lista de argumentos, en la lista de parámetros en este caso. Vamos a decir, oh, 42 fue pasado a x, así que encontramos a este tipo, encontramos a este, nos alineamos, decimos, sí, ese es el t. Diremos que 42 es un número, por lo tanto t es un tipo número, por lo tanto es como si hubieras escrito f de número, por lo tanto x es un número. Genial. La charla ha terminado. Todos pueden irse a casa.

3. Algoritmo para Inferir el Argumento de Tipo

Short description:

Ahora escribamos el algoritmo para inferir el argumento de tipo. Usaremos las funciones 'box' y 'unbox' como ejemplos. Al alinear los tipos de los argumentos y parámetros, podemos determinar el argumento de tipo. Si solo hay un candidato, es sencillo. Pero si hay varios candidatos, necesitamos comparar el valor en la caja con el valor x dado.

Genial. Ahora, escribamos el algoritmo aquí. Miremos el tipo de argumento donde vemos el parámetro de tipo utilizado en el nivel superior y simplemente lo usamos. Pero, ¿qué pasa si no hay un parámetro de tipo utilizado en el nivel superior de nuestra firma de función? Podría aparecer en otro lugar.

Así que tengo un tipo aquí, caja de t, bastante sencillo. Solo tiene una única propiedad llamada valor de tipo t. Lo usaremos mucho. Es un gran tipo para usar. Tengo descaja de t. Esta es otra buena función. Es un gran uso de los genéricos porque toma una caja de t y devuelve el t desde dentro de ella. Así que hace la gran cosa que deberías hacer con los genéricos, que es relacionar tus tipos de entrada con tus tipos de salida o un tipo de entrada con otro tipo de entrada. Normalmente no estaré escribiendo cuerpos de función aquí solo para ahorrar espacio en pantalla, pero puedes imaginar la implementación de descaja. Simplemente devuelve B.value. Y llamamos a descaja y pasamos valor colon 42.

Así que necesitamos averiguar qué va en este argumento de tipo que estamos infiriendo. Una cosa que podrías hacer es simplemente hacer un proceso de alineación, donde dices, vale, vimos valor colon t en el argumento b. Vimos valor colon número aquí abajo. Ese es el tipo de esta expresión. Simplemente alineamos el t y el número. No hablaré exactamente de cómo se hace eso, pero puedes imaginar cómo funciona. Por lo tanto, t es un tipo número, por lo tanto, n es un número. Muy bien. Podríamos escribir eso como en cada una de las posiciones de los parámetros, buscaremos ts y lo alinearemos con los tipos de argumento y estableceremos t de acuerdo a esas coincidencias que encontramos.

Necesito acortar este texto a medida que lo cambiamos durante el transcurso de la masterclass. Hablaré del paso uno como la recolección de candidatos haciendo ese proceso de coincidencia, y estableceremos t a un candidato del conjunto que encontramos. Pero un candidato es insatisfactorio. Eso parece subespecificado, así que si solo hay un candidato, eso es sencillo, pero ¿qué pasa si hay varios candidatos? Caja, sin cambios en este ejemplo, pero ahora tenemos caja tiene valor de t. Toma una caja, que es una caja de t, y toma una x, que es una t, y te dice si el valor en la caja coincide con la x que le diste. Lo llamas y dices valor colon 42 y la cadena hola es tu valor x.

4. Toma de Decisiones en la Inferencia de Tipo

Short description:

Tenemos dos candidatos: número y cadena. Podemos elegir 'desconocido' como el argumento de tipo, lo que haría que la llamada tenga éxito. Alternativamente, podríamos inferir 'cadena o número' como el argumento de tipo, permitiendo que la llamada tenga éxito. Sin embargo, inferir 'número' resultaría en un error en hello. Para tomar una decisión, consideremos cómo se vería la llamada si la incluimos en línea. La inclusión en línea revelaría que comparar cadenas y números no es plausible. Probablemente pretendías comparar un número con la longitud de hello o encontrar otra forma de alinear los tipos. Aunque la llamada debería dar error, podrías argumentar que inferir 'cadena o número' es una inferencia válida. Pero esto podría ser un argumento circular.

Recolectemos algunos candidatos. Recogemos número de valor colon 42, recogemos cadena de x. Así que ahora tenemos dos candidatos y necesitamos decidir qué hacer. Podríamos decir que no hay realmente nada en común entre número y cadena, así que simplemente iremos a desconocido. Actuaremos como si hubieras escrito caja tiene valor de desconocido, lo que significa que valor colon 42 es un desconocido válido y hello es un desconocido válido. La llamada tendrá éxito. Eso es bueno. Podríamos decir que es cadena o número. Es un tipo un poco más específico. Nuevamente procesaríamos esta llamada y diríamos OK, es como si hubieras escrito caja tiene valor de cadena o número. Valor colon 42 es válido, caja es cadena o número y hello es una cadena o número válida. La llamada debería tener éxito. O podríamos decir que T debería ser número y eventualmente obtendrías un error en hello porque si hubiéramos dicho caja tiene valor de número, no te estaríamos permitiendo pasar esa cadena a la posición de número. Entonces, ¿qué deberíamos hacer? Tenemos tres respuestas válidas que parece que podríamos elegir, pero dos de ellas no dan error y una sí. Dar error parece malo. Pero, ¿qué deberíamos hacer? Te ofreceré un principio que creo que es útil cuando estás pensando en genéricos, que es que generalmente deberían imitar lo que sucedería si hubieras incluido la llamada a esa función. Así que por incluir me refiero a reemplazar la llamada con una recitación del cuerpo de la función. Así que si tenemos esta llamada que teníamos antes, si la incluimos, se vería así. Diría que valor colon 42 se reemplaza aquí. Eso está reemplazando caja aquí arriba, dot value corresponde aquí y luego hello va aquí. Esto no es cómo funciona TypeScript internamente. Siempre estamos razonando sobre las cosas en términos de la firma de la función. Lo que te estoy mostrando aquí es solo una forma de pensar en cómo deberían funcionar los genéricos. Así que TypeScript diría, si esto es lo que estaba sucediendo, que no puedes comparar cadenas y números. Eso parece no ser una pieza plausible de código para escribir. Siempre va a devolver falso. Probablemente quisiste escribir algo como este valor, que es un número, triple igual a hello dot length o alguna otra forma de hacer que estas cosas se alineen de una manera que plausiblemente se superpongan. Así que parece que esta llamada debería dar error, pero podrías volver y decir, sabes, Ryan, T como cadena o número es una inferencia realmente buena. Deberías hacer eso porque si hubieras incluido esto pero tratado estos valores como si fueran de tipo cadena o número, entonces se habría permitido. Así que tal vez esto es solo un argumento circular y Ryan está haciendo puntos muy malos.

5. Algoritmo para Inferir Argumentos de Tipo

Short description:

Para asegurar que se capta la intención del desarrollador, el algoritmo para inferir argumentos de tipo recoge candidatos y elige el primero. El proceso de inferencia tiene como objetivo determinar lo que el desarrollador habría escrito. Se anima a escribir explícitamente los argumentos de tipo para definir el comportamiento de la inferencia. La elección del primer candidato por parte del algoritmo puede parecer arbitraria, pero asegura la mejor respuesta correcta. Se da otro ejemplo con múltiples candidatos, donde se elige el primer candidato, lo que lleva a un error en la segunda posición. Las implementaciones plausibles no habrían errado en esta llamada.

A eso, diría que no parece ser el comportamiento correcto porque la intención del desarrollador existe. Al menos por ahora, los humanos están escribiendo este código. Los humanos tienen intención y la forma en que estructuras tu código nos informa de esa intención. Pensemos en algunos ejemplos contrarios de cómo podrías haber escrito caja como valor. Si hubieras querido que el tipo de la caja y el tipo de la X no tuvieran ninguna relación entre sí, para eso existen los múltiples argumentos de tipo. Podrías haber dicho que esto tiene dos parámetros de tipo T y U, y que no hay relación entre estos y pueden ser inferidos libremente sin tener en cuenta el uno al otro. O si realmente querías que las cosas fueran lo que fueran, puedes simplemente decir caja de desconocido y X colon desconocido. No hay necesidad de usar genéricos en absoluto si no estás tratando de relacionar lo que está en esta posición con lo que está en esta posición.

Así que nuestro algoritmo se convierte en recoger candidatos, elegir el primero y luego procesar la llamada, pensamos que escribiste esos argumentos de tipo con un propósito. Así es como se ve. Decimos que es como si hubieras escrito caja tiene valor de número por lo que no podemos convertir esta cadena al número aquí. De nuevo, este es el proceso de inferencia, siempre estamos tratando de averiguar lo que habrías escrito si te hubiéramos hecho escribir esto. Y si te apetece escribir caja tiene valor de desconocido aquí, siempre eres libre de escribir estos argumentos de tipo explícitamente. Se trata de cuál debería ser el comportamiento de la inferencia. No siempre se trata de la respuesta correcta, se trata de la mejor respuesta. La mejor respuesta correcta, supongo. Así que nuestro algoritmo aquí es que estamos recogiendo los candidatos, eligiendo el primero y procesando la llamada. Pero primero es bastante extraño. ¿Por qué dije primero y no último? ¿Por qué no el del medio? ¿Por qué no el penúltimo? Primero es bastante arbitrario aquí, ¿verdad?

Así que veamos otro ejemplo de múltiples candidatos. Esta vez tengo find de t. Le das una aguja de tipo t y un array de t, que es tu pajar. Y luego, como dije, no hay cuerpos de función. Puedes imaginar lo que hace find, ¿verdad? Tal vez llama a pajar dot incluye aguja. Así que llamas a find y pasas hola y el 1, 2, 3, hola. Así que es una cadena en esta posición y un array de cadena o número. Así que pasamos por recoger candidatos. Tenemos una cadena de la aguja y una cadena o un número del pajar. Elegimos el primero, que es t de tipo cadena. Y luego eso implicaría que cometemos un error en el array de cadena o número que aparece en la segunda posición. Si pensamos en nuestra inclusión, aunque no podemos ver el cuerpo de find, cualquier implementación plausible que podamos pensar realmente no habría errado en esta llamada.

6. Determinando el Mejor Supertipo Común

Short description:

Necesitamos elegir el mejor candidato de nuestra lista, no solo el primero. El concepto que utilizamos es el mejor supertipo común, que es el tipo más grande que contiene a todos los demás. Si tenemos string, number y el tipo unión, string o number o Boolean, elegiríamos el tipo unión como el mejor supertipo común. Si no hay un mejor supertipo común, tendremos que hacer algo arbitrario. En el ejemplo dado, la llamada tiene éxito porque el mejor supertipo común entre los candidatos es string o number. Si los candidatos fueran solo números, la llamada sería sospechosa y resultaría en un error.

Entonces, parece que necesitamos hacer algo mejor aquí. Necesitamos elegir el mejor candidato de nuestra lista, no solo el primero. ¿Qué significa mejor? Eso es solo aplazar el problema, ¿verdad? Entonces, ¿cómo determinamos lo mejor? El concepto que utilizamos se piensa como el mejor supertipo común, que es el más grande tipo de los candidatos que es el que contiene a todos los demás. Si tenemos string, number y el tipo unión, string o number o Boolean, este último tipo, el tercero aquí, contiene a todos los demás. String aparece aquí, y number aparece aquí. Este es un supertipo de los otros dos. Así que elegiríamos este y diríamos, si estos fueran nuestros candidatos, este debería ser el que elegimos. Si tienes pez, o mamífero, o animal, queremos elegir animal, ese va a ser el que contiene a los demás. Si tienes string y number como tus candidatos, no hay un mejor supertipo común. Todavía tendremos que hacer algo un tanto arbitrario aquí, pero no vamos a formar una unión automáticamente aquí por las razones que hablamos anteriormente. Entonces, en este ejemplo, recogemos nuestro candidato string de hello. Recogemos string o number del array, y el mejor supertipo común entre esos es string o number. Es el candidato que contiene a todos los demás candidatos. Ese es el que elegimos, y la llamada tiene éxito. Así que eso es realmente bueno. Si solo hubieras tenido números en ese array, ahora esta llamada parece sospechosa. Parece que algo ha salido mal. Así que el candidato uno es un string. El candidato dos es un number. Volvemos a elegir el primero, tal vez. Así que diremos que T es string, y entonces eso significa que obtendremos un error en nuestro array de números porque esperábamos un array de strings en ese caso. Así que eso parece bueno. Así que estamos recogiendo nuestros candidatos, estamos eligiendo el mejor supertipo común, y estamos procesando la llamada.

7. Recolección de Candidatos y Determinación de Prioridad

Short description:

El paso de collectCandidates puede fallar al determinar la inferencia de tipo. En el ejemplo, el algoritmo elige el candidato 'caja de número' en lugar de 'número', que es la respuesta correcta. Para resolver esto, necesitamos considerar la prioridad de los candidatos. Los candidatos recolectados de una ocurrencia de T más anidada son mejores que los de un nivel superior. Al asignar prioridad y seleccionar el mejor supertipo de mayor prioridad, podemos determinar la correcta inferencia de tipo. Este enfoque se utiliza en la nueva versión de la función 'find', que devuelve un valor aleatorio de tipo T de un array basado en una condición dada.

Creo que el paso de collectCandidates está un poco subespecificado, y veamos cómo eso puede salir mal. Tengo box de nuevo, y he escrito una nueva función llamada unboxIfBox. Es un gran nombre. O le das una caja de T y te devolverá el T de dentro de la caja, o le das cualquier otra cosa y te devolverá esa misma cosa. Así que tal vez le das un string, simplemente te devuelve ese mismo string.

Llamamos a unboxIfBox y pasamos una caja de número aquí, y vamos a nuestra fase de recolección de candidatos. Así que trabajamos de izquierda a derecha en esta unión, y de T, recogemos el candidato caja de número porque eso es lo que se mostró aquí. Luego recogemos número, de T, aquí, donde aparece como cajas de T. No hay un mejor supertipo común, así que elegimos el primero arbitrariamente, decimos que es una caja de número. Eso no parece correcto. Por un lado, eso no es útil en absoluto, e incluso si hubieras dicho, bueno, el algoritmo debería ser, deberíamos unir estos, vamos a hacer uniones de múltiples candidatos, eso es lo mejor que hacer. Todavía no tiene sentido decir que X es de tipo número o caja de número, porque el contrato de esta función simplemente no implica realmente que va a hacer eso, ¿verdad? Número es la única respuesta correcta aquí.

Si simplemente miras a los candidatos, realmente no puedes averiguar qué hacer. Necesitas otra información sobre cómo obtuviste esos candidatos. Tienes que hacer una observación que es que los candidatos que recolectas de una ocurrencia más anidada de T siempre van a ser mejores que los que aparecen a un nivel alto. El peor caso de una inferencia de tipo que puedes hacer es este T independiente, y caja de T es mejor que eso. Significa que has coincidido con algo más específico porque significa que estas cosas se alinearon, y siempre deberías preferir este T sobre ese T si ambos existen. Estamos hablando de la prioridad a medida que avanzamos, así que recogemos una inferencia de menor prioridad del T independiente. Recogemos número de donde aparece dentro de la caja. Esa es una inferencia de mayor prioridad. Y luego decimos bien, vamos a tomar todos los candidatos que empaten por el más alto y elegir el mejor supertipo común de esos. Solo hay una inferencia de alta prioridad que coincide aquí, así que elegimos número y eso es bueno. Así es como resolvemos ese problema. A medida que recogemos los candidatos, anotamos la prioridad, y luego elegimos el mejor supertipo de mayor prioridad de esa lista. Puedes mirar esto y decir, hey Ryan, ¿por qué estamos eligiendo supertipo? ¿Por qué no subtipo? ¿Qué está pasando aquí? Es una gran pregunta. Tengo una nueva versión de find. Todavía es un argumento de tipo, find de T. Toma un array de T y una función que acepta un T y devuelve un Boolean. Si nos ponemos nuestros sombreros de imaginación e intentamos pensar en lo que hace, probablemente devuelve, digamos, algún valor aleatorio de tipo T del array donde la función o tal vez donde la función devuelve falso. Así que tengo esta función isNeat, toma strings o números, te dice si son ordenados o no, y luego llamamos a find y le pasamos este array de números, y estamos buscando algo que sea ordenado de esa lista.

8. Inferencia y Algoritmo para Parámetros de Tipo

Short description:

Recogemos un candidato del array, que es número. Recogemos otro candidato de isNeat, que es string o número. Vemos que T es de tipo string o número, por lo que X es de tipo string o número. La otra información que debes tener en cuenta aquí es si la T aparece en una posición covariante o contravariante. Nuestro nuevo algoritmo es algo que solo puedo demostrar realmente con una función más complicada. Vamos a llamar a find, vamos a pasar un array de string o número en este primer array, un array de números en el segundo array, y luego nuestra función predicado en func aquí toma strings o números o Booleans. Vamos a hacer alguna inferencia, vamos a recoger algunos candidatos. Lo que parecemos querer elegir es string o número, así que lo que vamos a hacer para obtener ese resultado es que vamos a tratar las posiciones de entrada como un límite superior en el resultado, y luego vamos a hacer nuestro algoritmo de supertipo común mejor entre los otros candidatos de salida que tenemos.

Vamos a hacer alguna inferencia. Recogemos un candidato del array, que es número. Recogemos otro candidato de isNeat, que es string o número. Ambos aparecen a un nivel de profundidad en la anidación, por lo que tienen la misma prioridad. Así que vamos a elegir el mejor supertipo común entre ellos. Eso es string o número. Así que ese es el valor que obtenemos.

Vemos que T es de tipo string o número, por lo que X es de tipo string o número. Parece correcto. Pero luego lo miras un poco, y te preguntas, espera un minuto, ¿qué hace string en X? No le dimos ningún string a find. IsNeat no puede producir strings, solo puede aceptar strings. Find en runtime no tiene forma de inspeccionar find y averiguar cuál es su tipo de argumento, por lo que no parece plausible que alguien que tiene un array de números de repente pueda producir un string de la nada, no tiene sentido. Si solo miras esto por un tiempo y te quedas mirando estos candidatos, como antes, no puedes simplemente mirar estos candidatos y averiguar qué hacer a menos que tengas alguna otra información.

La otra información que debes tener en cuenta aquí es si la T aparece en una posición covariante o contravariante, o podríamos hablar de esto en términos de entrada o posiciones de salida. Estos Ts del array están entrando en find y estos valores que estamos pasando a arg desde dentro de find están saliendo de find. Así que realmente necesitamos pensar en las posiciones de entrada y salida de los parámetros de tipo mientras los recogemos. Nuestro nuevo algoritmo es algo que solo puedo demostrar realmente con una función más complicada. He hecho que find tome dos arrays, y he hecho que isNeat también acepte Booleans. Pido disculpas por que sea un poco más largo, pero vamos a llamar a find, vamos a pasar un array de string o número en este primer array, un array de números en el segundo array, y luego nuestra función predicado en func aquí toma strings o números o Booleans. Hagamos una pequeña tabla, voy a condensar el ejemplo de código para que puedas verlo todo en pantalla a la vez.

Vamos a hacer alguna inferencia, vamos a recoger algunos candidatos. Voy a empezar en arg en realidad, así que vamos a alinear arg colon t a este valor de string o número Boolean, y eso nos va a dar nuestro candidato de entrada. Vamos a recoger de nuestro primer array t, que es un string o número, y nuestro dos nos da un array de números. Así que tenemos esta lista de candidatos aquí. Lo que parecemos querer elegir es string o número, así que lo que vamos a hacer para obtener ese resultado es que vamos a tratar las posiciones de entrada como un límite superior en el resultado, y luego vamos a hacer nuestro algoritmo de supertipo común mejor entre los otros candidatos de salida que tenemos. Así que nuestro mejor resultado es string o número, que está por debajo, en términos de Teoría de Tipos, de este límite superior de string o número o Boolean, y procederemos con la llamada como si hubieras llamado find o string o número. Eso significa que Boolean no aparece en X, lo cual es definitivamente el comportamiento correcto. Si modifico un poco este ejemplo, y digo que isNeat solo acepta números y luego paso un array de números y un array de strings, queremos emitir un error aquí, y idealmente el error que emitiríamos actuaría como si hubieras llamado isNeat de A e isNeat de B. Ese es el error en este programa, ¿verdad? Sabemos por la firma de find que es capaz de pasar un T de Array2 a func en la posición de arg, así que necesitamos emitir un error como si hubieras llamado isNeat de A, porque eso es lo que está pasando aquí, así que probemos nuestro algoritmo. Recogemos nuestro mejor candidato de salida de un string o un número en ese array, pero luego encontramos un límite superior de isNeat, porque vemos n colon número en una posición de entrada, y por lo tanto nos quedamos con número, como estamos infiriendo aquí.

9. Sensibilidad al Contexto y Actualización del Algoritmo

Short description:

De este otro array, obtenemos una salida inferior y un error. Lo limitamos en función de la posición de entrada, por lo que no puedes convertir strings en números. Nuestro algoritmo actualizado recoge candidatos, anota la varianza y elige el mejor candidato en ambas direcciones. La sensibilidad al contexto se trata de la función exec con argumentos de productor y consumidor que devuelven T y U respectivamente. Es una función de una línea con una firma genérica.

Y luego, de este otro array, obtenemos una salida inferior. Entonces, obtenemos un error. Decimos, está bien, es como si hubieras llamado a find de número, porque lo limitamos en función de esta posición de entrada. Por lo tanto, no puedes convertir este string o ese string en números. Obtienes un error realmente bueno aquí, así que eso es genial. Así que nuestro algoritmo actualizado se convierte en recoger los candidatos, anotar la varianza y luego elegir el mejor candidato limitando en ambas direcciones. Por último, quiero hablar de lo que llamamos sensibilidad al contexto. Es un poco complicado. Tengo una nueva función para ti aquí, que es exec de T y U. Tiene un argumento de productor que produce una T. Tiene un consumidor que toma una T y produce una U. Y luego devuelve la U que tu consumidor produjo. Así que si imaginamos el cuerpo de la función que esta firma genérica implica, dice que exec invocará al productor, obtendrá un valor de tipo T. Pasará esa T al consumidor, en esta posición de argumento, que devolverá una U y luego exec devolverá esa U. Así que es una especie de función de una línea, pero esa es su firma genérica.

10. Algoritmo para Expresiones Sensibles al Contexto

Short description:

Vamos a invocarlo, con nuestro productor siendo una función que produce hello. Y nuestra función consumidora aceptará hello y devolverá hello dot length. Así que pensamos que n será 1, 2, 3, 4, 5. Hagamos algunas inferencias. Procedemos de izquierda a derecha en ausencia de cualquier otro algoritmo, y decimos que recojamos del consumidor en la primera posición, o equivalentemente, busquemos la primera T en esta lista.

Vamos a invocarlo, con nuestro productor siendo una función que produce hello. Y nuestra función consumidora aceptará hello y devolverá hello dot length. Así que pensamos que n será 1, 2, 3, 4, 5. Hagamos algunas inferencias. Procedemos de izquierda a derecha en ausencia de cualquier otro algoritmo, y decimos que recojamos del consumidor en la primera posición, o equivalentemente, busquemos la primera T en esta lista. La T que vemos aquí es para arg, y arg aquí abajo no tiene ninguna anotación de tipo, y no hay nada que nos diga qué tipo debería ser arg. Aquí podría haber un tipo contextual, pero no lo hay porque estamos en medio de inferir qué debería ser T. Así que recogemos un candidato para T, que es como, es implícitamente Any. Quizás lo descartamos. Quizás no lo pensamos. No importa realmente para el propósito de este ejemplo, porque seguimos para recoger para U. Y decimos que arg.length es lo que estamos infiriendo para U. Si arg es de tipo Any, entonces Any.length es de tipo Any. Así que por lo tanto, el candidato que obtenemos para U es Any. Recogemos un candidato para el tipo de retorno del productor T, que es string, y por lo tanto decimos que U es Any. Ya sea que, ya sabes, cómo tratas con el Any implícito y los candidatos de string para T es inmaterial aquí porque significa que N es de tipo Any. Y eso es realmente insatisfactorio, porque miras este código y miras este código y dices, no veo por qué TypeScript no pudo averiguar qué hacer con esto. Así que lo que tienes que averiguar es, arg puede obtener un tipo basado en su contexto, así que esta expresión, puede cambiar de tipos basado en donde aparece en tu programa. Para esta función, no tanto. No tiene ningún argumento. Solo devuelve un string. Así que lo que podemos hacer es procesar estos argumentos fuera de orden mirando si o no son sensibles al contexto en el que están. Así que nuestro nuevo algoritmo va a ser que recojamos los candidatos de las expresiones no sensibles al contexto. Vamos a mirar todos los parámetros de tipo para los que hemos recogido al menos un candidato, y vamos a escoger el mejor de ahí. Y simplemente vamos a anotar eso y luego seguir adelante. Repetiremos el proceso para nuestras expresiones sensibles al contexto y luego procesaremos la llamada. ¿Cómo se ve eso? Hagamos algunas inferencias. Hacemos nuestra inferencia primero haciendo nuestro paso. Está mirando los argumentos no sensibles al contexto. Aquí, tenemos t colon string subiendo desde hello.

11. Algoritmo de Inferencia de Expresiones Sensibles al Contexto

Short description:

Así que hemos terminado con todas nuestras expresiones insensibles al contexto ahora. Anotamos t como string y procesamos arg como string. Cuando buscamos el tipo contextual para arg, sabemos que es string basado en t. Esto nos permite procesar arg.length y usar el tipo number. El algoritmo de inferencia de expresiones sensibles al contexto utiliza ejemplos de código para determinar la intención del usuario y proporcionar la mejor inferencia. Pensar en el diseño del lenguaje en términos de ejemplos motivadores aporta claridad y garantiza un comportamiento óptimo.

Así que hemos terminado con todas nuestras expresiones insensibles al contexto ahora. Así que anotamos que t es string. Vamos a procesar arg. Y decimos, ah, arg es de tipo string. Voy a anotar number más tarde, pero anoté string para t. Así que ahora cuando estoy buscando el tipo contextual para arg, sé que es string, basado en t y el string que anotamos antes. Eso me permite procesar arg.length. Por lo tanto, uso del tipo number. Y todo simplemente funciona. Puedes pensar en qué hacer con t en esta posición particular. No tiende a importar ya que simplemente no lo hace. Si trabajas en por qué, verás por qué. Pero así es como funciona.

Así que ese es nuestro algoritmo de inferencia de expresiones sensibles al contexto. En resumen, espero no haberte asustado demasiado. Creo que todo esto es bastante sencillo. Teníamos algunos ejemplos de código bastante básicos, y al mirar esos ejemplos de código y cómo fueron escritos, pudimos averiguar cuál era la intención del usuario, y pudimos usar eso para hacer la inferencia correcta. Simplemente ser correcto no es suficiente. También tienes que obtener la mejor inferencia. Mostré muchos ejemplos donde había de dos a cuatro respuestas correctas para elegir, pero sólo una mejor respuesta. La inferencia es realmente como un examen de opción múltiple, donde no sólo tienes la tarea de encontrar una buena respuesta, tienes la tarea de encontrar la mejor respuesta. Si piensas en tu diseño de lenguaje en términos de tus ejemplos motivadores, creo que eso aporta una tremenda claridad, porque si simplemente vas y tratas de escribir un algoritmo sin mirar cómo actúan los ejemplos que te importan, es posible que no obtengas el mejor comportamiento. Así que siempre esté mirando los ejemplos y usándolos para guiar cómo tomas decisiones sobre tu inferencia y todo lo demás sobre tu lenguaje. Que tengas un gran día.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

TypeScript and React: Secrets of a Happy Marriage
React Advanced Conference 2022React Advanced Conference 2022
21 min
TypeScript and React: Secrets of a Happy Marriage
Top Content
TypeScript and React are inseparable. What's the secret to their successful union? Quite a lot of surprisingly strange code. Learn why useRef always feels weird, how to wrangle generics in custom hooks, and how union types can transform your components.
React's Most Useful Types
React Day Berlin 2023React Day Berlin 2023
21 min
React's Most Useful Types
Top Content
We don't think of React as shipping its own types. But React's types are a core part of the framework - overseen by the React team, and co-ordinated with React's major releases.In this live coding talk, we'll look at all the types you've been missing out on. How do you get the props type from a component? How do you know what ref a component takes? Should you use React.FC? And what's the deal with JSX.Element?You'll walk away with a bunch of exciting ideas to take to your React applications, and hopefully a new appreciation for the wonders of React and TypeScript working together.
Stop Writing Your Routes
Vue.js London 2023Vue.js London 2023
30 min
Stop Writing Your Routes
The more you keep working on an application, the more complicated its routing becomes, and the easier it is to make a mistake. ""Was the route named users or was it user?"", ""Did it have an id param or was it userId?"". If only TypeScript could tell you what are the possible names and params. If only you didn't have to write a single route anymore and let a plugin do it for you. In this talk we will go through what it took to bring automatically typed routes for Vue Router.
Making Magic: Building a TypeScript-First Framework
TypeScript Congress 2023TypeScript Congress 2023
31 min
Making Magic: Building a TypeScript-First Framework
I'll dive into the internals of Nuxt to describe how we've built a TypeScript-first framework that is deeply integrated with the user's IDE and type checking setup to offer end-to-end full-stack type safety, hints for layouts, middleware and more, typed runtime configuration options and even typed routing. Plus, I'll highlight what I'm most excited about doing in the days to come and how TypeScript makes that possible not just for us but for any library author.
Full-stack & typesafe React (+Native) apps with tRPC.io
React Advanced Conference 2021React Advanced Conference 2021
6 min
Full-stack & typesafe React (+Native) apps with tRPC.io
Top Content
Why are we devs so obsessed with decoupling things that are coupled nature? tRPC is a library that replaces the need for GraphQL or REST for internal APIs. When using it, you simply write backend functions whose input and output shapes are instantly inferred in your frontend without any code generation; making writing API schemas a thing of the past. It's lightweight, not tied to React, HTTP-cacheable, and can be incrementally adopted. In this talk, I'll give a glimpse of the DX you can get from tRPC and how (and why) to get started.
Faster TypeScript builds with --isolatedDeclarations
TypeScript Congress 2023TypeScript Congress 2023
24 min
Faster TypeScript builds with --isolatedDeclarations
Top Content
Type-checking a TypeScript codebase can be slow, especially for monorepos containing lots of projects that each need to use the type checker to generate type declaration files. In this talk, we introduce — for the very first time — a new TypeScript feature we are working on called “Isolated Declarations” that allows DTS files to be generated without using the type checker at all! This opens the door to faster declaration generation in TypeScript itself, as well as in external tools written in other languages such as ESBuild and swc. You'll see how to use this new option, and maybe (just maybe) you’ll be convinced about the benefits of explicit return types! Most importantly, we will show how Isolated Declarations enables parallel builds to spread work across your CPU cores to significantly improve the build speed of your TypeScript projects.

Workshops on related topic

React, TypeScript, and TDD
React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Top Content
Featured WorkshopFree
Paul Everitt
Paul Everitt
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
Best Practices and Advanced TypeScript Tips for React Developers
React Advanced Conference 2022React Advanced Conference 2022
148 min
Best Practices and Advanced TypeScript Tips for React Developers
Top Content
Featured Workshop
Maurice de Beijer
Maurice de Beijer
Are you a React developer trying to get the most benefits from TypeScript? Then this is the workshop for you.In this interactive workshop, we will start at the basics and examine the pros and cons of different ways you can declare React components using TypeScript. After that we will move to more advanced concepts where we will go beyond the strict setting of TypeScript. You will learn when to use types like any, unknown and never. We will explore the use of type predicates, guards and exhaustive checking. You will learn about the built-in mapped types as well as how to create your own new type map utilities. And we will start programming in the TypeScript type system using conditional types and type inferring.
Deep TypeScript Tips & Tricks
Node Congress 2024Node Congress 2024
83 min
Deep TypeScript Tips & Tricks
Top Content
Workshop
Josh Goldberg
Josh Goldberg
TypeScript has a powerful type system with all sorts of fancy features for representing wild and wacky JavaScript states. But the syntax to do so isn't always straightforward, and the error messages aren't always precise in telling you what's wrong. Let's dive into how many of TypeScript's more powerful features really work, what kinds of real-world problems they solve, and how to wrestle the type system into submission so you can write truly excellent TypeScript code.
Practice TypeScript Techniques Building React Server Components App
TypeScript Congress 2023TypeScript Congress 2023
131 min
Practice TypeScript Techniques Building React Server Components App
Workshop
Maurice de Beijer
Maurice de Beijer
In this hands-on workshop, Maurice will personally guide you through a series of exercises designed to empower you with a deep understanding of React Server Components and the power of TypeScript. Discover how to optimize your applications, improve performance, and unlock new possibilities.
 
During the workshop, you will:
- Maximize code maintainability and scalability with advanced TypeScript practices
- Unleash the performance benefits of React Server Components, surpassing traditional approaches
- Turbocharge your TypeScript with the power of Mapped Types
- Make your TypeScript types more secure with Opaque Types
- Explore the power of Template Literal Types when using Mapped Types
 
Maurice will virtually be by your side, offering comprehensive guidance and answering your questions as you navigate each exercise. By the end of the workshop, you'll have mastered React Server Components, armed with a newfound arsenal of TypeScript knowledge to supercharge your React applications.
 
Don't miss this opportunity to elevate your React expertise to new heights. Join our workshop and unlock the potential of React Server Components with TypeScript. Your apps will thank you.
Advanced TypeScript types for fun and reliability
TypeScript Congress 2022TypeScript Congress 2022
116 min
Advanced TypeScript types for fun and reliability
Workshop
Maurice de Beijer
Maurice de Beijer
If you're looking to get the most out of TypeScript, this workshop is for you! In this interactive workshop, we will explore the use of advanced types to improve the safety and predictability of your TypeScript code. You will learn when to use types like unknown or never. We will explore the use of type predicates, guards and exhaustive checking to make your TypeScript code more reliable both at compile and run-time. You will learn about the built-in mapped types as well as how to create your own new type map utilities. And we will start programming in the TypeScript type system using conditional types and type inferring.
Are you familiar with the basics of TypeScript and want to dive deeper? Then please join me with your laptop in this advanced and interactive workshop to learn all these topics and more.
You can find the slides, with links, here: http://theproblemsolver.nl/docs/ts-advanced-workshop.pdf
And the repository we will be using is here: https://github.com/mauricedb/ts-advanced
Mastering Node.js Test Runner
TestJS Summit 2023TestJS Summit 2023
78 min
Mastering Node.js Test Runner
Workshop
Marco Ippolito
Marco Ippolito
Node.js test runner is modern, fast, and doesn't require additional libraries, but understanding and using it well can be tricky. You will learn how to use Node.js test runner to its full potential. We'll show you how it compares to other tools, how to set it up, and how to run your tests effectively. During the workshop, we'll do exercises to help you get comfortable with filtering, using native assertions, running tests in parallel, using CLI, and more. We'll also talk about working with TypeScript, making custom reports, and code coverage.