7

I have a Project (Existing Project) for which frontend is writen in namespaced typescript. I cannot rewrite all typescript file from namespaced approach to export/import es6 syntax.

However the below code is working fine. and no issue if i add my new ts module or react component as far i wrap it in namespace and call it by Class or with namespace prefix if its in other namespace.

Issue is i cannot use external modules with import statement.How can i use..?since import statement is throwing error.

To be more specific for below code i wanted to use react-redux,

When i check the node_moduels folder i saw @types folder from where the React namesapce is referred I Assume Due to this line in tsconfig

"typeRoots": [
      "./node_modules/@types/"
    ]

But when i install npm install --save @types/react-redux it also created react-redux folder with index.ts under @types folder in node modules. but it doesnt have the Namespace export lines how the React type file has some thing like below.

export as namespace React;

declare namespace React {
....
}

Ultimately..How can i use third party node moudles, especially react-redux in this project. Or any simple node module and access it in any ts file simple moudles such as "react-bootstrap" Below is the simple ts file for react componnet wrapped in namesapce whihc is working fine (except import staement)

import { createStore } from 'redux'; //if i add this line this is throwing error.

namespace Profile.Sample {

    import { createStore } from 'redux'; //this too throwing error, without these lines my code is working fine.
    export class Profiles extends React.Component<any> {

        constructor(props) {
            super(props);           
            this.state = { brand: "Ford" };

        }     

        render(): React.ReactNode {

            return (
                <sect
                    <div className="container-fluid">
                        <div className="row">
                            <div className="col-lg-12">
                                <div className="main-title">
                                    <h2>Profiles</h2>
                                    <p>Lorem ipsum dolor sit amet, consectetur adi</p>
                                </div>
                            </div>
                        </div>
                        
                    </div>
                </section>

            );
        }
    }
}

Below is my tsconfig

{
  "compileOnSave": true,
  "compilerOptions": {
    "preserveConstEnums": true,
    "experimentalDecorators": true,
    "declaration": true,
    "emitBOM": true,
    "jsx": "react",
    "noEmitHelpers": true,
    "inlineSourceMap": true,
    "inlineSources": true,
    "outFile": "wwwroot/Scripts/site/Profiles.js",
    "skipLibCheck": true,
    "skipDefaultLibCheck": true,
    "target": "es5",
    "typeRoots": [
      "./node_modules/@types/"
    ]
  }
}

I know namespace is not the recommended approach going forward..but this is an existing project and cannot rewrite all files from namespace to import/export statements.

4
  • Do you bundle your code with any bundler - webpack, rollup, parcel? Commented Oct 10, 2020 at 7:25
  • Nope, just build js is called as a script in the project master file Commented Oct 10, 2020 at 9:53
  • Is the tsconfig.json file fixed or modifiable? Commented Oct 11, 2020 at 7:48
  • its modifiable..but existing codes shouldn't break. Commented Oct 11, 2020 at 13:38

1 Answer 1

9
+50

Ok, let's make it runnable step by step:

  1. Although you did not state it explicitly, I assume you get the error message Cannot compile modules using option 'outFile' unless the '--module' flag is 'amd' or 'system'.. The option outFile generates one master file with all your code concatenated in it. Since you are using "target": "es5" in your tsconfig.json, the default value for module is commonjs. commonjs is the module syntax widely used for node.js (var x = require('x') / exports.x = x) and this syntax does not support merging code into one file, therefore it is prohibited. But you want to use your code in the browser, so commonjs is not supported there anyways. So the first thing to do is

    Insert "module": "AMD" into your tsconfig.json file.

    AMD is a module syntax usable in the browser. You can also use "UMD", which is just an extension, so that the module supports AMD and commonjs modules.

  2. To avoid some more configuration later on, you should also

    Change "outFile": "wwwroot/Scripts/site/Profiles.js" to "outDir": "wwwroot/Scripts/site" in your tsconfig.json file.

  3. Setting module to AMD changes the default of moduleResolution from node to classic, but that's not good, because then import {createStore} from 'redux' will fail with the error message Cannot find module 'redux'. Did you mean to set the 'moduleResolution' option to 'node', or to add aliases to the 'paths' option?. Therefore

    Include "moduleResolution": "node" into your tsconfig.json file.

  4. In your code you define a class which extends another base class. If you transpile this inheritance, a special function __extends is required. With the current setup this function is not generated by typescript and you get the error Uncaught ReferenceError: __extends is not defined. Therefore

    Remove "noEmitHelpers": true.

  5. Now all your files can be transpiled to AMD modules. But to use those modules, you need a module loader. The most popular one is require.js. To use it,

    Insert <script data-main="setup" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" integrity="sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg==" crossorigin="anonymous"></script> into your HTML file.

    You can also download it and serve it yourself. data-main="setup" means that after require.js has been loaded, the script setup.js is executed. This script is created in the next step.

  6. require.js needs some configuration to find all the modules. So

    Create wwwroot\Scripts\site\setup.js:

    requirejs.config({
      appDir: ".",
      paths: { 
        'react': ['./node_modules/react/umd/react.production.min.js', 'https://unpkg.com/react@16/umd/react.production.min'],
        'react-dom': ['./node_modules/react-dom/umd/react-dom.production.min.js', 'https://unpkg.com/react-dom@16/umd/react-dom.production.min'],
        'redux': ['./node_modules/redux/dist/redux.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min'],
      },
    });
    
    requirejs(['entryPoint'], function() {
      console.log("Entry point 'index' has been loaded!");
      return {};
    });
    

    In the first part require.js is configured. Especially, the locations of external modules (modules installed via npm) are defined. Here the locations of react, react-dom and redux are set to the locations in the node_modules folder with external files from content delivery networks (CDN) as backups. You can also remove one of the locations. They are requested from left to right and moving on if one request fails. If you use the locations in your node_modules folder, you must serve them from your webserver as well!

    In the second part the module entryPoint is executed. This is the entry point to your app. Change the name to whatever you want to be your entry point. If you want the module defined in the file someOtherFile.js to be the entry point, then write requirejs(['someOtherFile'], ....

  7. To transpile all files run

    tsc --project tsconfig.json.

  8. Serve all files in the directory wwwroot/Scripts/site including your HTML file. For testing purposes you can use

    npx serve.

You can see the slightly modified code here: https://codesandbox.io/s/elated-fermat-ie2ms?file=/src/index.tsx.

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

3 Comments

Thanks a lot for the excellent step-by-step explanation. Two things, 1)Cant i create a single js file from the above approach and avoid require an entry point. Cox i am struggling with the entry point setup. 2)the moment i add 'export' to existing namespace eg export namespace Profile.Sample { ....} it started throwing error for other namespaces ." cannot find namespace XYZ" eg . and none of my namespace had the export prefix in project
1) Even if you transpile to a single file, you will have to define an entry point. This entry point defines what is run on startup. No entry point = no code to be run. 2) I actually forgot about this one, I added the export for testing purposes. If you don't export your namespaces, then you also can't import all stuff in it. Then how do you manage your namespaces in your code? Do you have only one namespace?
Thanks a lot again @Peter, there are 2-3, each is called with a namespace prefix when need to be accessed from another namespace. The question is how can refer external libs without exporting namespace. the reason is the moment I add 'export' the namespace prefix referred starts throwing error. There is only one root namespace and multiple child namespace. Eg codes in Profiles.Agent name space can refer the class from Profiles.Client Namespace without issue. and any name spaces in nodemodules/@types can also be referred.

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.