Búsqueda de sitios web

Error de TypeScript ts(2322) incluso después de la comprobación `instanceof`


Estoy intentando escribir una función que devuelva una nueva instancia de un tipo que se selecciona en función del tipo del argumento de la función.

Aquí está el código:

class A {}
class A1 extends A {}
class A2 extends A {}

class B<TypeA extends A>
{
    a: TypeA;
    constructor(a: TypeA)
    {
        this.a = a;
    }
}
class B1 extends B<A1> {}
class B2 extends B<A2> {}

function createB<TypeA extends A>(a: TypeA): B<TypeA>
{
    if (a instanceof A1)
    {
        const a1: A1 = a;  // OK
        return new B1(a1); // Error - ts(2322): 'A1' is assignable
                           // to the constraint of type 'TypeA',
                           // but 'TypeA' could be instantiated
                           // with a different subtype of constraint 'A'
    }
    else if (a instanceof A2)
    {
        const a2: A2 = a;  // OK
        return new B2(a2); // Error - ts(2322): 'A2' is assignable
                           // to the constraint of type 'TypeA',
                           // but 'TypeA' could be instantiated
                           // with a different subtype of constraint 'A'
    }
    else
    {
        throw new Error();
    }
}

Área de juego de TypeScript

El compilador de TypeScript dice que "'TypeA' podría ser instanciado con un subtipo diferente de la restricción 'A'" pero la verificación a instanceof A1 asegura que fue instanciado con el tipo A1 y no con algún otro subtipo de A. ¿Es esto una limitación de TypeScript o me estoy perdiendo de algo?

Respuesta #1

Si más tarde se crea un subtipo A1B de A1, una instancia de A1B también será una instancia de A1:

class A1B extends A1 {}
const a1b = new A1B()
console.log(a1b instanceof A1); // true

Y si uno llama a createB(a1b), dado el tipo de retorno de createB, esperamos que el resultado sea un B<A1B>, mientras que tu implementación devolvería un B1 (es decir, B<A1>).

const result = createB(a1b);
//    ^? B<A1B>

Eso es lo que significa el mensaje de error.


En tu caso, podrías ser más restrictivo con lo que tu función puede producir, por ejemplo, usando sobrecargas de función en lugar de genéricos:

function createB2(a: A1): B1;
function createB2(a: A2): B2;
function createB2(a: A1|A2): B<A1|A2> {
    if (a instanceof A1) {
        const a1: A1 = a;
        return new B1(a1); // Okay
    }
    else if (a instanceof A2) {
        const a2: A2 = a;
        return new B2(a2); // Okay
    }
    else {
        throw new Error();
    }
}

const result2 = createB2(a1b);
//    ^? B1

Enlace del parque