Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using NPM Modules in Namespaced Typescript project

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.

like image 330
user3815413 Avatar asked Oct 07 '20 10:10

user3815413


People also ask

Can you use npm packages in TypeScript?

via npm. You can use npm to install TypeScript globally, this means that you can use the tsc command anywhere in your terminal. To do this, run npm install -g typescript . This will install the latest version (currently 4.7).

Can I use module exports in TypeScript?

In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).

How do I use TypeScript modules?

A module can be created using the keyword export and a module can be used in another module using the keyword import . In TypeScript, files containing a top-level export or import are considered modules. For example, we can make the above files as modules as below. console.

What are namespaces and modules in typescript?

Namespaces and Modules. A note about terminology: It’s important to note that in TypeScript 1.5, the nomenclature has changed. “Internal modules” are now “namespaces”. “External modules” are now simply “modules”, as to align with ECMAScript 2015’s terminology, (namely that module X { is equivalent to the now-preferred namespace X {).

Is it possible to write NPM module in typescript?

We have succeeded in writing and publishing an npm module in TypeScript. The great thing that we achieved here is that our module can now be seamlessly used in JavaScript or TypeScript projects without any need of running. Feel free to reach out if you have any problems.

What is the difference between namespaces and modules?

Namespaces are a TypeScript-specific way to organize code. Namespaces are simply named JavaScript objects in the global namespace. This makes namespaces a very simple construct to use. Unlike modules, they can span multiple files, and can be concatenated using outFile .

What are namespaces in JavaScript?

Namespaces are simply named JavaScript objects in the global namespace. This makes namespaces a very simple construct to use. They can span multiple files, and can be concatenated using --outFile.


Video Answer


1 Answers

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.

like image 109
Peter Lehnhardt Avatar answered Oct 11 '22 13:10

Peter Lehnhardt