首页 > 解决方案 > 静态只读成员赋值 => TypeError: XXX is not a constructor

问题描述

我遇到了带有静态只读成员的 Typescript/Jest 的问题。这里是一个基本的例子。

项目结构基于此启动项目:https ://github.com/alexjoverm/typescript-library-starter ,如果它可以帮助重现问题。

点.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)
    });
});

错误

运行测试套件时,我得到

● 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)

这个错误是什么意思?

标签: javascripttypescriptjestjstypeerror

解决方案


如果您将控制台日志添加到以下位置,则该错误更容易理解pointutil.ts

import {Point} from "./point";

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

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

这表明Pointis undefined(它不是构造函数:-) 发生这种情况的原因是相互导入point.tspointUtil.ts创建循环模块依赖项。它与测试无关,因为任何导入的模块都会触发错误point.ts

当模块point.ts被评估并触发 的评估时pointUtil.ts,导入的值PointpointUtil.ts是不确定的,直到point.ts模块的评估完成。但是,由于静态origin属性的定义相当于做

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

这意味着Pointget 之前pointUtil.ts(因此point.ts)已被评估,导致错误。

Modulepoint.ts也使用 import from pointUtil.ts,但这是在dummyCalc方法内部,因此在初始模块评估期间不会对其进行评估。这意味着如果您在inpointUtil.ts之前导入,则and的评估顺序会颠倒过来,并且错误将消失,因为在最初的 undefined 上不会失败。point.tsrepl.test.tspoint.tspointUtil.tspoint.tsPointUtil

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

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

虽然这是一个 hacky 解决方案,所以最好避免循环并将立即需要的定义放在Point模块point.ts本身中。事实上,无论如何origin更适合作为静态属性。Point


推荐阅读