Búsqueda de sitios web

javascript - Asignación de miembro estático de solo lectura => TypeError: XXX no es un constructor


Estoy enfrentando un problema con Typescript/Jest con un miembro estático de solo lectura. Aquí un ejemplo básico.

La estructura del proyecto se basa en este proyecto inicial: https://github.com/alexjoverm/typescript-library-starter si puede ayudar a reproducir el problema.

point.ts

import {PointUtil} from "./pointUtil";

export class Point
{
    public x: number;
    public y: number;

    public constructor(x: number, y: number)
    {
        this.x = x;
        this.y = y;
    }

    public dummyCalc() : number
    {
        return this.x + this.y + PointUtil.origin.x + PointUtil.origin.y;
    }
}

pointutil.ts

import {Point} from "./point";

export class PointUtil {
    public static readonly origin: Point = new Point(12, 2);
}

repl.test.ts

import {Point} from "../src/point";

describe("REPL test", () => {
    it("dummy test", () => {
        expect(new Point(1, 1).dummyCalc()).toEqual(16)
    });
});

Error

Cuando ejecuto el conjunto de pruebas estoy obteniendo

● Test suite failed to run

TypeError: _point.Point is not a constructor

  3 | export class PointUtil {
  4 |   public static readonly origin: Point = new Point(12, 2);
> 5 | }
  6 | 

  at src/pointUtil.ts:5:24
  at Object.<anonymous> (src/pointUtil.ts:7:2)
  at Object.<anonymous> (src/point.ts:1:4237)
  at Object.<anonymous> (test/repl.test.ts:1:1181)

¿Qué significa este error?

  • La versión de Typescript es 2.7.2
  • jest 22.0.2
  • ts-jest 22.0.0

Respuesta #1

El error es más fácil de entender si agregas un registro en la consola a pointutil.ts:

import {Point} from "./point";

console.log('Point:', Point);

export class PointUtil {
    public static readonly origin: Point = new Point(12, 2);
}

Esto muestra que Point es undefined (lo cual no es un constructor :-) La razón por la que esto ocurre es que point.ts y pointUtil.ts se importan mutuamente y crean una dependencia circular de módulos. No está relacionado con la prueba, ya que el error se desencadenaría por cualquier módulo que importe point.ts.

Cuando se evalúa el módulo point.ts, y esto provoca la evaluación de pointUtil.ts, el valor del Point importado en pointUtil.ts será indefinido hasta que la evaluación del módulo point.ts haya terminado. Sin embargo, dado que la definición de la propiedad estática origin equivale a hacer

PointUtil.origin = new Point(12, 2);

significa que Point se utiliza antes de que pointUtil.ts (y por lo tanto point.ts) hayan sido evaluados, lo que conduce al error.

El módulo point.ts también usa una importación de pointUtil.ts, pero esto está dentro del método dummyCalc, por lo que no se evalúa durante la evaluación inicial del módulo. Esto significa que si importas pointUtil.ts antes de point.ts en repl.test.ts, el orden de evaluación de point.ts y pointUtil.ts se invierte, y el error desaparecerá, ya que point.ts no fallará con un PointUtil inicialmente indefinido.

import './pointUtil'
import {Point} from './point'

describe("REPL test", () => {
  ..
});

Sin embargo, esta es una solución improvisada, por lo que es mejor evitar el ciclo y poner las definiciones que requieren inmediatamente Point en el propio módulo point.ts. De hecho, origin es más adecuado como una propiedad estática en Point de todos modos.