JavaScript和Typescript中的模块导入之间有区别吗?

时间:2018-07-21 22:46:32

标签: typescript runtime-error es6-modules custom-element

我将脚趾插入Shadow DOM / Custom元素中,以为我发现JS和TS如何处理模块化导入之间存在古怪之处,或者我做错了什么?

我的主要JS文件看起来像这样...

// import classes (and register custom elements)
import { Game } from './engine/game.js';
import { MainMenuState } from './menu/main-menu-state.js';

// get the game custom element from the DOM and create the main menu state
const oGame = document.querySelector('test-game');
const oState = document.createElement('main-menu-state');

// push the state onto the game
oGame.setState(oState);

模块看起来像这样...

export class Game extends HTMLElement {

    constructor() {
        super();
        this._shadowRoot = this.attachShadow({
            mode: 'open'
        });
    }

    setState(oState) { ... }

    pushState(oState) { ... }

    popState() { ... }

}

if(!customElements.get('test-game')) {
    customElements.define('test-game', Game);
}

正如您在模块底部看到的那样,我正在使用Game类定义自定义的 test-game 元素。 主菜单状态元素的定义与此类似。

这可以正常工作,并且代码可以按我期望的那样执行

一旦我将此代码转换为Typescript,就会遇到问题。

主要TS文件看起来像这样...

import { StateInterface } from './engine/state.interface.js';

import { Game } from './engine/game.js';
import { MainMenuState } from './menu/main-menu-state.js';

const oGame: Game = document.querySelector('test-game') as Game;
const oState: StateInterface = document.createElement('main-menu-state') as MainMenuState;

oGame.setState(oState);

TS模块看起来很熟悉...

export class Game extends HTMLElement implements GameInterface {

    public constructor() {
        super();

        this._shadowRoot = this.attachShadow({
            mode: 'open'
        });
    }

    public setState(oState: StateInterface): void { ... }

    public pushState(oState: StateInterface): void { ... }

    public popState(): void { ... }

}

if(!customElements.get('test-game')) {
    customElements.define('test-game', Game);
}

但是这次浏览器控制台给我以下错误...

  

未捕获的TypeError:oGame.setState不是函数       在main.ts:9

我的tsconfig文件设置为使用ES6模块分辨率...

{
    "compilerOptions": {
        "module": "es6",
        "target": "es6",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "sourceMap": true,
        "alwaysStrict": true,
        "noUnusedLocals": true,
        "outDir": "./public/js",
        "rootDir": "./public/ts"
    }
}

除非TS编译器对模块导出做一些不同的事情,否则我只是不明白为什么这两段代码之间会有差异


编辑

好吧,看来这是由TS编译器引起的时髦。 只需对对象进行类型检查就可以理顺...

import { StateInterface } from './engine/state.interface.js';

import { Game } from './engine/game.js';
import { MainMenuState } from './menu/main-menu-state.js';

const oGame: Game = document.querySelector('test-game') as Game;
const oState: StateInterface = document.createElement('main-menu-state') as MainMenuState;

if(oGame instanceof Game && oState instanceof MainMenuState) {
    oGame.setState(oState);
}

1 个答案:

答案 0 :(得分:3)

在TypeScript中,当您导入某些内容但仅将其用作类型时,TS会在编译代码时将其保留。在这种情况下,导入将消失,这意味着元素不会被注册,因此,当您尝试查询自定义元素时,方法就会丢失。之所以在这里对值进行类型检查是因为导入的类实际上被用作instanceof的值,因此导入保持不变。

Here's an issue about it,以及一些解决方案。对于需要触发的副作用,请在入口JS文件顶部的import './engine/game.js'确保其始终运行并注册该元素。