What sets apart module imports in JavaScript and Typescript?

Exploring the realm of Shadow DOM and Custom elements, I've encountered an interesting discrepancy between how JavaScript (JS) and TypeScript (TS) handle modular imports. Am I missing something here?

My primary JS file has this structure...

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

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

// assign the state to the game
oGame.setState(oState);

The module itself is structured like this...

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

At the end of the module, you can see that I'm defining the custom test-game element using the Game class. The main-menu-state element follows a similar pattern.

This setup functions correctly, operating as expected.

However, when transitioning this code into TypeScript, an issue arises.

The main TS file takes this form...

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

With the TypeScript module exhibiting a familiar layout...

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

Despite the similarity, the browser console shows an error this time...

Uncaught TypeError: oGame.setState is not a function at main.ts:9

My tsconfig file specifies ES6 module resolution...

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

I fail to comprehend the reason behind the disparity in behavior unless the TS compiler handles module exports differently.


UPDATE

It seems the issue stems from the TS compiler. By simply checking the object types, the problem appears to be resolved...

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

Answer №1

When working with TypeScript, if you import a type without actually using it in your code, the import will be omitted during compilation. This can cause issues when querying for custom elements as the necessary methods may not be available due to the lack of registration. However, if the imported classes are used as values for instanceof, the import will be retained.

For more information and potential solutions, check out this related issue. To ensure that any required side effects occur, consider adding import './engine/game.js' at the beginning of your main JS file to guarantee registration of the element.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Develop a user interface designed specifically for a subset of JSX.Elements or ReactElement

For better organization, I decided to create an interface called IconInterface to group all my icons: import { IconProps, CaretProps, CheckboxProps } from "./IconProps"; interface IconInterface { (props: IconProps | CaretProps | CheckboxProp ...

Tips for showing an image through a button in Angular 4

Currently, I am in the process of creating a simple website and I have encountered an issue. When I click on a button to display an image, it works fine. However, when I click on another button to display a different image, the previous image remains visib ...

Issues with Vite's global import feature not functioning properly in a production build

My current setup involves loading all markdown files within a directory using a glob import. The code snippet below depicts this functionality: const useGetChangelogs = () => { const [changelogs, setChangelogs] = useState<string[]>([]); useEf ...

Encountering a type error when attempting to filter TypeORM 0.3.5 by an enum column that is

I have the following configuration: export enum TestEnum { A = 'A', B = 'B' C = 'C' } @Entity() export class User { @PrimaryGeneratedColumn() id: number @Column({enum: TestEnum}) test: TestEnum } ...

Working with Typescript and JSX in React for event handling

I'm currently facing an issue with updating the state in a React component I'm developing using TypeScript (React with Addons 0.13.3, Typescript 1.6.0-dev.20150804, definition file from ). /// <reference path="react/react-addons.d.ts" /> i ...

Modify the data type of an object member based on its original type

I am seeking to convert the data type of each member in an object based on the specific member variable. Here is an example: class A { fct = (): string => 'blabla'; } class B { fct = (): number => 1; } class C { fct = (): { o ...

What types of modifications do ViewChildren and ContentChildren QueryLists keep an eye out for?

Imagine you come across the following lines of code: https://i.stack.imgur.com/7IFx1.png And then, in a different section, you stumble upon this code block: https://i.stack.imgur.com/qac0F.png Under what circumstances would () => {} be executed? Wha ...

Is the Angular Karma test failing to update the class properties with the method?

I am struggling to comprehend why my test is not passing. Snapshot of the Class: export class Viewer implements OnChanges { // ... selectedTimePeriod: number; timePeriods = [20, 30, 40]; constructor( /* ... */) { this.selectLa ...

Guide to implement editable columns in Angular 4 with a click functionality

I have a table displaying records using ngFor, and I am looking to enable editing of a column upon clicking it. <tr *ngFor="let cd of descriptionCodes; let i = index"> <td><input type="checkbox"></td> <td> {{cd.code}} ...

Using TypeScript - Implementing a generic constraint to allow passing a Zod schema result as an argument to a function

I'm in the process of creating a custom controller function to streamline my application. The repetitive task of wrapping try-catch, parsing a zod schema, and merging the request zod schema into a single object is present in all handler functions. The ...

Is there a way to retrieve a compilation of custom directives that have been implemented on the Vue 3 component?

Is there a way to retrieve the list of custom directives applied to a component? When using the getCurrentInstance method, the directives property is null for the current component. I was expecting to see 'highlight' listed. How can I access the ...

How can you enhance a component by including additional props alongside an existing onClick function?

As a newcomer to React and TypeScript, I've created a simple component that looks like this: const CloseButton = ({ onClick }: { onClick: MouseEventHandler }) => { const classes = useStyles(); return <CloseIcon className={classes.closeButto ...

Creating dummy objects from a specific data type in Typescript for the purpose of testing

I'm exploring ways to streamline the creation of mock data for unit testing within an Angular solution. Currently, I am defining interfaces such as: export interface ReferenceDataItemCommon { codeDescription?: string; code: string; deleted?: boo ...

A simple method in JavaScript/TypeScript for converting abbreviations of strings into user-friendly versions for display

Let's say I am receiving data from my backend which can be one of the following: A, B, C, D Although there are actually 20 letters that could be received, and I always know it will be one of these specific letters. For example, I would like to map A ...

Can a single data type be utilized in a function that has multiple parameters?

Suppose I have the following functions: add(x : number, y : number) subtract(x : number, y : number) Is there a way to simplify it like this? type common = x : number, y : number add<common>() This would prevent me from having to repeatedly define ...

Specialized Character Formats in TypeScript

In my quest to enhance the clarity in distinguishing different types of strings within my program - such as absolute paths and relative paths, I am seeking a solution that ensures functions can only take or return specific types without errors. Consider t ...

Guide to managing MUI's theme typography font weight choices with TypeScript

I am interested in learning how to incorporate a new font weight into my theme, for example, fontWeightSemiBold, and disable the existing fontWeightLight and fontWeightMedium. I believe this can be achieved through module augmentation. For reference, there ...

What steps can I take to correct my code so that it only shows a single table?

I'm facing an issue while trying to display my dynamic JSON data. It's rendering a table for each result instead of all results in the same table. My array data is coming from the backend API. const arr = [ { "Demo": [ ...

Why is it that the HttpClient constructor in Angular doesn't require parameters when instantiated through the constructor of another class, but does when instantiated via the 'new' keyword?

I am trying to create a static method for instantiating an object of a class, but I have encountered a problem. import { HttpClient } from '@angular/common/http'; export MyClass { // Case 1 public static init(): MyClass { return this(new ...

What steps should be taken to prepare data for transmission to a server in a Next.js environment?

I'm in the process of creating a website that requires authentication. I am using Next (React) and typescript for web development. My objective is to make most pages ServerSideRendered or StaticHTML. However, I encountered an issue right at the begin ...