Búsqueda de sitios web

iterador - ¿Por qué los genéricos IterableIterator<> y Generator<> de TypeScript son ligeramente diferentes?


En TypeScript (3.6.3) Generator<> es casi idéntico a IterableIterator<>. Cuando Generator<> extiende Iterator<>, asigna por defecto el tercer argumento genérico (TNext) a unknown. Iterator<> por sí solo asigna por defecto TNext a undefined. Así que Generator e Iterator (y IterableIterator) no coinciden tan bien como deberían.

let gen2:IterableIterator<string>;

function* gen1():Generator<string> {
    yield* gen2;
}

La línea yield* es un error: "No se puede delegar la iteración al valor porque el método 'next' de su iterador espera el tipo 'undefined', pero el generador contenedor siempre enviará 'unknown'. ts(2766)".

¿Me estoy perdiendo de algo? ¿Hay una buena razón para esto?

Respuesta #1

Este es en realidad un asunto muy complicado. No pretendo comprenderlo completamente. Pero quizás pueda ofrecer algún entendimiento.

¿Por qué los agregaron?

(en este commit). Typescript decidió, para bien o para mal, que quiere verificaciones de tipos más estrictas para los generadores. De hecho, hay algunas razones válidas para esto. Tomemos el siguiente ejemplo

function* foo() {
    let m = 0;
  
    while (m < 10) {
      yield m++;
    }
    
    return "done";
}

let gen = foo(),
    curr;

while(!(curr = gen.next()).done) {}

// At his point we should know that 
// curr.value is a string because curr.done is true

Aquí podemos ver el problema: no podemos saber si un valor fue devuelto o producido todavía según todas las reglas de la lógica que deberíamos. Así que introdujeron TReturn. TNext fue introducido para:

[…] verificar correctamente y proporcionar un tipo para el resultado de una expresión yield basado en el siguiente tipo de la anotación de tipo de retorno del generador (es decir, el tipo TNext en la definición de Generator anterior).

¿Por qué los valores predeterminados?

Ahora, si has decidido hacer tales cambios, probablemente vas a romper algo de código; el objetivo es romper lo menos posible.

Debemos notar que existen diferencias idiomáticas en el uso de la función next() en generadores y en iteradores que no son generadores. Como señala ECMA-262 respecto a los iteradores.

Se pueden pasar argumentos a la función next, pero su interpretación y validez dependen del Iterador de destino. La sentencia for-of y otros usuarios comunes de Iteradores no pasan ningún argumento, por lo que los objetos Iterator que esperan ser usados de esa manera deben estar preparados para ser llamados sin argumentos.

Los iteradores se utilizan predominantemente en bucles for-of, los cuales no pasan un argumento a next. De hecho, es muy raro pasar un argumento a la función next (MDN incluso lo llama "una función sin argumentos"). Por lo tanto, la única opción sensata para el valor predeterminado de TNext sería undefined. Hacerlo unknown sería un gran obstáculo para la comprobación de tipos (sin mencionar el código compilado con --strictNullChecks).

Todo eso estaría genial si pasar un argumento a la función next() con generadores no fuera una práctica bastante común: en realidad, tiene un caso de uso válido… y un comportamiento definido en el estándar:

Generator.prototype.next(valor)

El método next realiza los siguientes pasos:

  1. Sea g el valor este.
  2. Devolver ?GeneratorResume(g, value, vacío).

y en MDN:

El valor para enviar al generador.

El valor se asignará como resultado de una expresión yield. Por ejemplo, en variable=yield expression, el valor pasado a la función .next() se asignará a variable.

Sin mencionar que, en un caso de uso típico, la primera llamada a .next() se haría sin un argumento y las siguientes se harían con uno. Desafortunadamente, no hay forma de especificar un tipo del tipo "opcional la primera vez, desconocido las siguientes veces", así que supongo que a la luz de todo esto decidieron usar unknown para el TNext en los Generadores.

Por supuesto que no hay una situación perfecta. Pero tienen que conformarse con lo que creen que sería menos problemático.

Todos esos problemas se discuten en este número para cualquiera que esté interesado.

Artículos relacionados