JavaScript 和 Typescript 中模块导入的区别?
Difference between module imports in JavaScript and Typescript?
我正在尝试 Shadow DOM / 自定义元素,我认为我发现了 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);
}
正如您在模块底部看到的,我正在使用游戏 class 定义自定义 test-game 元素。 main-menu-state 元素以类似的方式定义。
这工作正常,代码按照我的预期执行
一旦我将这段代码转换成 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);
}
但是这次浏览器控制台给我以下错误...
Uncaught TypeError: oGame.setState is not a function
at 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);
}
在 TypeScript 中,当您导入某些内容但仅将其用作类型时,TS 在编译您的代码时会忽略导入。在这种情况下,导入消失意味着元素未注册,因此当您尝试查询自定义元素时,这些方法会丢失。在这里对值进行类型检查的原因是因为导入的 类 实际上被用作 instanceof
的值,因此导入保留。
Here's an issue about it, 也有一些解决方案。对于需要触发的副作用,入口 JS 文件顶部的 import './engine/game.js'
将确保它始终运行并注册元素。
我正在尝试 Shadow DOM / 自定义元素,我认为我发现了 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);
}
正如您在模块底部看到的,我正在使用游戏 class 定义自定义 test-game 元素。 main-menu-state 元素以类似的方式定义。
这工作正常,代码按照我的预期执行
一旦我将这段代码转换成 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);
}
但是这次浏览器控制台给我以下错误...
Uncaught TypeError: oGame.setState is not a function at 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);
}
在 TypeScript 中,当您导入某些内容但仅将其用作类型时,TS 在编译您的代码时会忽略导入。在这种情况下,导入消失意味着元素未注册,因此当您尝试查询自定义元素时,这些方法会丢失。在这里对值进行类型检查的原因是因为导入的 类 实际上被用作 instanceof
的值,因此导入保留。
Here's an issue about it, 也有一些解决方案。对于需要触发的副作用,入口 JS 文件顶部的 import './engine/game.js'
将确保它始终运行并注册元素。