1

Overview
I'm new to using TypeScript and PIXI.js without a framework. I used to use the language/library for work where we used namespaces with the module keyword. I've been trying to replicate the setup we used as I think it looks a lot better than having loads of import statements.

The Problem
When I run my code in the browser, I get the following error in the windows inspector console:

Uncaught TypeError: MyApp.AbstractModel is not a constructor
    at Main../src/Main.ts.Main.createModel (Main.ts:16)

What I've Found & Attempted Fixes
I have read through the typescript documentation for Namespaces found here and here loads of times but cannot see where I am going wrong.

Initially I thought the issue was that not all files were being loaded by webpack but I fixed that with suggestions found here (namely the "require.context()") and still the error persists.

I've tried importing the namespace from the main file (copied what's in Main to Game.ts, and put the following in Main.ts)

require.context("../src/", true, /\.ts$/);
/// <reference path="Game.ts" />

class Main {

    constructor() {
        console.log("Main.constructor()");
        new MyApp.Game();
    }

}
new Main();

The Structure
My file structure is as follows:

stack-overflow-example
| - .idea
| - dev
| | - dts
| | | <all .d.ts files from tsc compilation>
| | - js
| | | <all .js files and accompanying .js.map files from compiled .ts files>
| | - index.html
| - node_modules
| - src
| | - graphics
| | | - pixi-manager.ts
| | - model
| | | - AbstractModel.ts
| | - Main.ts
| | - refs.ts
| package.json
| package-lock.json
| tsconfig.json
| webpack.config.js

The Code
index.html

<!DOCTYPE html>
<html>
    <head>
        <title>stack-overflow-example</title>
    </head>
    <body>
        <script src="js/Bundle.js" type="text/javascript"></script>
    </body>
</html>

Maint.ts

/// <reference path="refs.ts" />
require.context("../src/", true, /\.ts$/);

namespace MyApp {
    export class Main {

        public static instance: Main = new Main();
        public model: AbstractModel;

        constructor() {
            console.log("Main.constructor()");
            this.createModel();
        }

        protected createModel(): void {
            this.model = new AbstractModel();
        }

        protected addComponent(view: PIXI.DisplayObject): void {
            PixiManager.instance.stage.addChild(view);
        }

        public getModel(): AbstractModel {
            return this.model;
        }
    }
}

AbstractModel.ts

/// <reference path="../refs.ts" />
namespace MyApp {
    export class AbstractModel {

        constructor() {
            console.log("AbstractModel.constructor()");
        }

    }
}

refs.ts

/// <reference path="graphics/pixi-manager.ts" />
/// <reference path="model/AbstractModel.ts" />
/// <reference path="Main.ts" />

package.json

{
  "name": "my-game",
  "version": "1.0.0",
  "description": "",
  "main": "Main.ts",
  "scripts": {
    "start": "webpack-dev-server --content-base dev/ --inline --hot"
  },
  "devDependencies": {...}
}

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./dev/js/",
    "module": "commonjs",
    "target": "ES5",
    "sourceMap": true,
    "declaration": true,
    "declarationDir": "./dev/dts"
  },
  "include": ["./src/*.ts"]
}

webpack.config.js

var path = require("path");
module.exports = {
    entry: {
        namespace: "./src/Main.ts"
    },
    mode: "development",
    devtool: "source-map",
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },
        ],
    },
    resolve: {
        extensions: [ '.tsx', '.ts', '.js' ],
    },
    output: {
        path: path.resolve(__dirname, "dev/js/"),
        filename: "js/Bundle.js"
    }
};

The Question
How can I fix the "MyApp.AbstractModel is not a constructor" error while keeping the namespace style rather than using modules?

All help will be really appreciated, thanks for your time.

1 Answer 1

0

First please check why is not a constructor error is happening: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_constructor . It seems that probably the AbstractModel is not defined where the error is happening.

You could check it like this:

        protected createModel(): void {
            console.log(typeof AbstractModel);
            this.model = new AbstractModel();
        }

If thats the case then you should change this question to something like: "How to load all classes from directory in TypeScript and WebPack?".

Then, the thing which you wanted to do using require.context(... aproach can be done as described in: Is it possible to import modules from all files in a directory, using a wildcard?

BUT please also read following docs :

As you see this is considered a bad practice by some people. I also prefer to explicitly enumerate all dependencies for current file. This way you can see exactly what is used where. Also the IDE should help you with that.

Next problem is <reference path... vs import usage. First you say:

... as I think it looks a lot better than having loads of import statements.

But then in refs.ts you do:

/// <reference path="graphics/pixi-manager.ts" />
/// <reference path="model/AbstractModel.ts" />
/// <reference path="Main.ts" />

so instead of using loads of import statements you have loads of reference statements :) . The number of code lines seems the same as when you would use imports.

Additionally please see again example from doc that you linked in your question: https://www.typescriptlang.org/docs/handbook/namespaces.html#multi-file-namespaces - in the Test.ts file there is <reference for each needed file :

/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
/// <reference path="ZipCodeValidator.ts" />

...

in files LettersOnlyValidator.ts and ZipCodeValidator.ts there is referenced /// <reference path="Validation.ts" /> - but in last file they need to reference /// <reference path="Validation.ts" /> again - so it means that "reference path approach" is not transitive.

This means that you should better rethink this and try to use import (and/or export) where needed.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.