1

I recently started integrating TypeScript and React with my companies legacy JavaScript code base. It's been very hit and miss, as I'm sure many of you understand.

After overcoming many of the big hurdles such as setting up a proper webpack process, understanding and setting up tsconfig files and breaking up the bundling properly, I've finally integrated it all with our current CI-pipeline.

However, now I'm having an issue with importing JavaScript libraries in my TypeScript code. I know it's bad practice, but it's sort of a middle-hand before moving to becoming fully TS-compliant.

I've got this basic component which builds fine:

import * as React from "react";
import * as ReactDOM from "react-dom";
import { Header } from "./components/header";
import * as $C from "./../../fortress.content.js";
import * as $F from "./../../fortress.js"; 

let url : string  = $F.writeUrl("Account", "LoginAjax");

ReactDOM.render(
    <div>
        <div> {url} </div>
    </div>,  
    document.getElementById("body")
);

$F.writeUrl allows us to essentially create a string based on a webconfig file in the root of the project, so basically a function taking a few different parameters and returning a string based on it:

However, when I run the transpiled JavaScript in a baremetal html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<div id="body"></div>
<div id="header"></div>
<body> 
    <script src="js/jquery-2.1.4.min.js"></script> 
    <script src="js/react-0.14.3.js"></script>
    <script src="js/react-dom-0.14.3.js"></script> 
    <script src="js/filemanager/filemanager.bundle.js"></script>
    <script src="js/anothermanager/anothermanager.bundle.js"></script>
    <script>  
            console.log("$F: ", $F);
            console.log("$C: ", $C);
    </script>
</body>
</html>

I get these errors:

Uncaught TypeError: Cannot read property 'bind' of undefined
    at new t (filemanager.bundle.js:1)
    at ReactCompositeComponentWrapper.mountComponent (react.js:5504)
    at ReactCompositeComponentWrapper.wrapper [as mountComponent] (react.js:12346)
    at Object.mountComponent (react.js:12971)
    at ReactDOMComponent.mountChildren (react.js:11685)
    at ReactDOMComponent._createContentMarkup (react.js:6817)

anothermanager.bundle.js:1 Uncaught ReferenceError: $C is not defined
    at anothermanager.bundle.js:1
    at Object.<anonymous> (anothermanager.bundle.js:1)
    at n (anothermanager.bundle.js:1)
    at Object.<anonymous> (anothermanager.bundle.js:1)
    at n (anothermanager.bundle.js:1)
    at anothermanager.bundle.js:1
    at anothermanager.bundle.js:1
    at ReactDOMComponent.mountComponent (react.js:6705)
    at Object.mountComponent (react.js:12971)
    at ReactCompositeComponentWrapper.mountComponent (react.js:5581)
    at ReactCompositeComponentWrapper.wrapper [as mountComponent] (react.js:12346)

index.html:17 Uncaught ReferenceError: $F is not defined
    at index.html:17

So I tried changing this to instantiate $C and $F in my TS:

let test = $F;
let test2 = $C;

This removed the above errors and actually shows me the $F and $C objects in my console log. But the problem now is that the $F.writeUrl("Account", "LoginAjax") is blowing as Chrome won't recognise it as a "function".

Have I done anything wrong with my imports?

4
  • 1
    Can you show how $F and $C are exported? Do those modules setup anything in the window object? Commented Jul 27, 2018 at 13:26
  • They aren't exported in the traditional sense. I read another question on SO about how you can skip the exporting by setting "allowSyntheticDefaultImports": true. The problem with those files is that there are dependencies that require them to stay JavaScript for now. Further, they are both IIFE enclosed (function( {})(), and I'm not sure how to export that? Commented Jul 27, 2018 at 13:29
  • So, at some point they set up window.$F or the likes? Commented Jul 27, 2018 at 13:36
  • 1
    Yeah, at the end in the IIFE scope. :) Commented Jul 27, 2018 at 13:44

2 Answers 2

3

I guess you don't need to import those files in your react bundle. If those files doesn't export anything with module.exports or export, and instead they just setup window members to be used later (which was in fact the standard years ago), as far as I know you should:

1) Load those files as scripts, so they are available everywhere. Be sure to add them before any react related script/bundle, so you're sure they are available when the react code mounts:

<script src="js/jquery-2.1.4.min.js"></script>
<script src="js/path-to-fortress-content.js"></script>
<script src="js/path-to-fortress.js"></script> 
<script src="js/react-0.14.3.js"></script>
<script src="js/react-dom-0.14.3.js"></script>

2) Now, they are available in your react code. You need to use them from window instead to let your bundler handle the $F/$C modules:

let $f: any = (window as any).$F;
let url : string  = $F.writeUrl("Account", "LoginAjax");
Sign up to request clarification or add additional context in comments.

3 Comments

I'm getting an error on let $f: any = window.$F; where my compiler (visual studio 2017) complains about $F not being defined on type window. Ideas?
I think you can just cast window to any to overcome this problem: let $f: any = (window as any).$F;
Lifesaver! Cheers! :D
1

[...] importing JavaScript libraries in my TypeScript code. I know it's bad practice, but it's sort of a middle-hand before moving to becoming fully TS-compliant

Don't worry, it's not necessarily a bad practice, especially during a transitionary period.

In a small addition to Sergeon's answer that covers the implementation problem, I'll add that some JavaScript libraries do have typings that allow you to use them in TypeScript applications, like jQuery:

npm i --saveDev "@types/jquery"

Then to use the typings in a .ts module:

import "jquery";
declare const $: JQueryStatic;

Which also gives you the benefit of some IDE IntelliSense assistance:

IntelliSense for jQuery

You can look for and add your other libraries from the @types/ repository, if you desire.

Comments

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.