2

I am relatively new to Typescript (well I've been trying to use it for years but never really understood some aspects). This issue has been bugging me for a while related to NPM imports. I am trying to use some web NPM modules in Typescript for dashjs and chartjs. Neither work when trying to import them and then reference them from a runner script.

The program file cannot seem to see the class SensedPlayback, implying that something isn't compiling properly but the JS file for SensedPlayback builds. Removing the import at the top of the SensedPlayback.ts file allows the program file to see it but then the imported module doesn't work. The intellisense for the module works within SensedPlayback.ts suggesting the module import is working fine.

Failed to find SensedPlayback class!

Any help would be greatly appreciated! Gerard

tsconfig.json

{
  "compileOnSave": true,
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "moduleResolution": "node",
    "module": "commonjs",
    "target": "es6",
    "outDir": "wwwroot/js/"
  },
  "exclude": [
    "node_modules",
    "wwwroot"
  ]
}

SensedPlayback.ts (with import)

import * as dashjs from "dashjs";

namespace TIMINGSRC {
    export declare var TimingObject: any;
    export declare var MediaSync: any;
}

class SensedPlayback {
    production: Production;

    videosDiv: HTMLDivElement;
    videos: dashjs.MediaPlayerClass[];
    seekHead: HTMLInputElement;
    playBtn: HTMLButtonElement;
    pauseBtn: HTMLButtonElement;

    _playbackTimeReciever: PlaybackTimeSync;

    _timingObject: any;
    _isSeeking: boolean;

    constructor(production: Production, videosDiv: HTMLDivElement, seekHead: HTMLInputElement, playBtn: HTMLButtonElement, pauseBtn: HTMLButtonElement) {
        this.production = production;

        this.videosDiv = videosDiv;
        this.seekHead = seekHead;
        this.playBtn = playBtn;
        this.pauseBtn = pauseBtn;

        this._setupPage();

        this._playbackTimeReciever = new PlaybackTimeSync();
    }

    start() {
        this._playbackTimeReciever.subscribe((tr) => {
            this.loadMediaForTime(tr.start);
        });

        this._playbackTimeReciever.start();
    }

    _setupPage() {
        this.playBtn.addEventListener('click', (event) => {
            this._timingObject.update({ velocity: 1.0 });
        });

        this.pauseBtn.addEventListener('click', (event) => {
            this._timingObject.update({ velocity: 0.0 });
        });

        this.seekHead.addEventListener('mousedown', (event) => {
            this._isSeeking = true;
        });

        this.seekHead.addEventListener('mouseup', (event) => {
            this._timingObject.update({ position: this.seekHead.value });
            this._isSeeking = false;
        });
    }

    loadMediaForTime(time: Date) {
        let takes = this.production.takes.filter((t) => {
            t.media.some((m) => m.startTime >= time
                && new Date(m.startTime.setMilliseconds(m.startTime.getMilliseconds() + m.duration)) < time)
        });

        let take = takes[0];
        this.videos = [];
        this.videosDiv.innerHTML = "";

        for (var m of take.media) {
            let video = <HTMLVideoElement>(document.createElement("video"));
            var player = new dashjs.MediaPlayerClass();
            player.initialize(video, URL.createObjectURL(`${m.url}/playback.mpd`), false);

            let wrapper = <HTMLDivElement>(document.createElement("div"));
            wrapper.classList.add("col-md-4");
            wrapper.classList.add("col-sm-6");

            let heading = <HTMLHeadingElement>(document.createElement("h4"));
            heading.innerText = this.production.cameras.find((c) => { return c.id == m.cameraId }).name;

            wrapper.appendChild(heading);
            wrapper.appendChild(video);

            this.videosDiv.appendChild(wrapper);
            this.videos.push(player);
        }

        this._configurePlayers();
    }

    _configurePlayers() {
        for (var v of this.videos) {
            v.on('loadstart', function (event) {
                v.setMaxAllowedBitrateFor('video', 4600);
            });

            v.on('loadedmetadata', (event) => {
                if (this._timingObject === undefined) {
                    this._timingObject = new TIMINGSRC.TimingObject({ range: [0, event.target.duration] });
                    this.seekHead.min = '0';
                    this.seekHead.step = '0.25';
                    this.seekHead.max = `${v.duration()}`;
                    this._timingObject.on('timeupdate', () => {
                        if (!this._isSeeking) {
                            this.seekHead.value = this._timingObject.clock.now();
                        }
                    });
                }
                var sync = new TIMINGSRC.MediaSync(event.target, this._timingObject);
            });
        }
    }
}

SensedPlaybackProgram.ts

let videosDiv = <HTMLDivElement>document.getElementById('cameras');
let seekHead = <HTMLInputElement>document.getElementById('seekhead');
let playBtn = <HTMLButtonElement>document.getElementById('play');
let pauseBtn = <HTMLButtonElement>document.getElementById('pause');

let production : Production = JSON.parse((<HTMLInputElement>document.getElementById('production')).value);

let playback = new SensedPlayback(production, videosDiv, seekHead, playBtn, pauseBtn);

playback.start();

1 Answer 1

2

When you compile a series of files that do not use import or export, the files are treated as not being modules, and all the symbols they declare end up in a global space. This is why when you remove your import statement, SensedPlaybackProgram.ts is able to see SensedPlayback even though it is defined in SensedPlayback.ts

When you add your import statement in SensedPlayback.ts, you turn it into a TypeScript module, and form then on, if you want to use a symbol it defines, you need to export it from this module, and import the symbol where you want to use it. So modify SensedPlayback.ts to have:

export class SensedPlayback {
...

And in SensedPlaybackProgram.ts, import it with:

import { SensedPlayback } from "./SensedPlayback";

(Adapt the path as needed.)

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

1 Comment

Perfect that was the problem. This was always something that frustrated me about Typescript. Admittedly with very little understanding of the inner workings but obscure things like this seem to happen. It seems to much of a black box to me, how can adding an import statement change the packaging of a file, without telling you clearly this is whats happening. Anyway thanks for your help!

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.