CorayThan
CorayThan

Reputation: 17835

Cannot access base class properties in Typescript

I've got a rather strange problem. I'm extending a class, and for some reason none of the extended class's properties or methods are accessible. Here's my class:

import "pixi";
import "p2";
import Phaser from "phaser";

export class MyGameState extends Phaser.State {
    // More stuff [...]
    create() {
        this.game.add.sprite(400, 300, "./assets/paddle.png");
    }
}

This actually does seem to successfully compile and run, but outputs an error about how it can't access game in this.game:

ERROR in ./src/my-game/MyGameState.ts (17,14): error TS2339: Property 'game' does not exist on type 'MyGameState'.

Intellij is also confused. It seems to recognize that create() is overriding a base class function, but also thinks game doesn't exist.

It's perfectly happy to access game off of Phaser.State inside that class, though, as long as it's not the extended version. So this compiles and works fine:

const workingState = new Phaser.State();
workingState.game.boot();

Here's a foreshortened version of the class in phaser.d.ts I'm extending:

declare module "phaser-ce" {
    export = Phaser;
}

declare class Phaser {
    class State {
        game: Phaser.Game;
        create(): void;
    }
}

And the horribly hairy configuration I've got to get Phaser working with Typescript this well ...

package.json

{
  "name": "MyGame",
  "main": "index.js",
  "scripts": {
    "dev": "webpack"
  },
  "devDependencies": {
    "browser-sync": "^2.18.12",
    "browser-sync-webpack-plugin": "^1.1.4",
    "expose-loader": "^0.7.3",
    "source-map-loader": "^0.2.1",
    "ts-loader": "^2.2.2",
    "tslint": "^5.5.0",
    "tslint-loader": "^3.5.3",
    "typescript": "^2.4.1",
    "webpack": "^2.6.1"
  },
  "dependencies": {
    "phaser-ce": "^2.8.1",
    "webfontloader": "^1.6.28"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "strictNullChecks": false,
    "module": "es6",
    "moduleResolution": "node",
    "target": "es5",
    "allowJs": true
  },
  "include": [
    "./src/"
  ]
}

webpack.config.js

var path = require('path')
var webpack = require('webpack')
var BrowserSyncPlugin = require('browser-sync-webpack-plugin')

// Phaser webpack config
var phaserModule = path.join(__dirname, '/node_modules/phaser-ce/')
var phaser = path.join(phaserModule, 'build/custom/phaser-split.js')
var pixi = path.join(phaserModule, 'build/custom/pixi.js')
var p2 = path.join(phaserModule, 'build/custom/p2.js')

var definePlugin = new webpack.DefinePlugin({
                                                __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true'))
                                            })

module.exports = {
    entry: {
        app: [
            path.resolve(__dirname, './src/main2.ts')
        ],
        vendor: ['pixi', 'p2', 'phaser', 'webfontloader']
    },
    devtool: 'source-map',
    output: {
        pathinfo: true,
        path: path.resolve(__dirname, 'dist'),
        publicPath: './dist/',
        filename: 'bundle.js'
    },
    watch: true,
    plugins: [
        definePlugin,
        new webpack.optimize.CommonsChunkPlugin(
            {name: 'vendor'/* chunkName= */, filename: 'vendor.bundle.js'/* filename= */}),
        new BrowserSyncPlugin({
                                  host: process.env.IP || 'localhost',
                                  port: process.env.PORT || 3000,
                                  server: {
                                      baseDir: ['./', './build']
                                  }
                              })
    ],
    module: {
        rules: [
            {test: /\.ts$/, enforce: 'pre', loader: 'tslint-loader', options: {emitErrors: true, failOnHint: true}},
            {test: /\.ts$/, loader: 'ts-loader'},
            {test: /pixi\.js/, use: ['expose-loader?PIXI']},
            {test: /phaser-split\.js$/, use: ['expose-loader?Phaser']},
            {test: /p2\.js/, use: ['expose-loader?p2']},
            {enforce: "pre", test: /\.js$/, loader: "source-map-loader"}
        ]
    },
    node: {
        fs: 'empty',
        net: 'empty',
        tls: 'empty'
    },
    resolve: {
        alias: {
            'phaser': phaser,
            'pixi': pixi,
            'p2': p2
        },
        extensions: [".ts", ".tsx", ".js", ".jsx"]
    }
}

Upvotes: 0

Views: 1216

Answers (2)

CorayThan
CorayThan

Reputation: 17835

It seems like the fancy combined phaser my webpack config creates must be broken in some way shape or form.

I discovered I could prevent the typescript error by importing phaser in a different way:

import * as Phaser from "phaser-ce";

When imported this way I do not get errors and can use the members of the base class.

Upvotes: 1

Fenton
Fenton

Reputation: 251162

This isn't a specific answer for Phaser, but more a general "where to start" answer.

Step one is, check out this:

export class MyGameState extends Phaser.State {
    // More stuff [...]
    create() {
        console.log(this);
        this.game.add.sprite(400, 300, "./assets/paddle.png");
    }
}

One of the most common gotcas in TypeScript (and JavaScript) is that this is not what you expect. This happens in TypeScript when a class method is invoked in a different context, such as an event.

If you find that this is an element, or some other context, you can probably solve the problem with a fat-arrow (=>), which preserves the lexical scope.

Upvotes: 1

Related Questions