2

I am using Typescript with Nodejs, Windows 7 Ultimate x64. Compiler is tsc.

I am modifying an existing codebase which has many files sharing a namespace. So, at the top of each file there is a declaration like so:

namespace program {
    ...
}

I am trying to get the library JSZip working in this existing codebase, but I was running into some weirdness that I think has to do with this namespace.

Before I get started, I want to describe how I set up the library. First I invoked npm like so to install:

npm install --save jszip
npm install --save @types/jszip

Then I set up my tsconfig.json to include this:

var map = {
    'jszip':                      'node_modules/jszip/dist/jszip.min.js'
};

I'm not sure that step matters but it might allow calling the import "jszip" down below.

Then I tried a few import commands in the code, but was hit with errors. First I tried:

import * as JSZip from 'jszip';

namespace program {
    ...
}

But this causes a large count of errors within that file. I get "namespace is declared but never used" warnings for that large namespace, at least within the file I am performing the import call in. Every reference to an external file within that namespace is also broken, and for those I get a "Cannot find name '...'" error. So calling the import like that seems to be incompatible with the use of a namespace.

So then I tried:

namespace program {
    import * as JSZip from 'jszip';

    ...
}

So, in this case I at least do not get file-wide errors, but that line is still creating errors. The two I get are "Import declarations in a namespace cannot reference a module." and "Cannot find module 'jszip'" (notably, I do not get the latter error when using the previous method.)

So this makes me think something is wrong in my approach or setup, but even though I am stuck at a relatively simple point I have not been able to find a resource yet to help understand the exact issue. Of course I found some resources that I've cobbled together a limited understanding with at least though, links here:

types/jszip: https://www.npmjs.com/package/@types/jszip

jszip: https://www.npmjs.com/package/jszip

This is a close example but not identical to my case. I don't think the solutions proposed will work in my situation, but I could be wrong: How to use namespaces with import in TypeScript

This is where I got the clue for the "import * ..." syntax and adding the "map" property to tsconfig: Import JSZip in Angular 2 project

Empty Set
  • 35
  • 6
  • What do you mean by `var map = ` in your tsconfig.json? Usually that file can contain only JSON not JavaScript. The example of using `map` that you linked to is doing that in a `systemjs.config.js` file not in a `tsconfig.json` file. I suggest removing that; it might just fix the error. – Shaun Luttin May 14 '19 at 04:22
  • Right, it seems I was misreading one of my references. I have modified it now to more or less match the example below. It is not resolving my error at its root though. It's strange I didn't get an error message for having completely off the wall code in tsconfig though. Is that an intentional feature? – Empty Set May 15 '19 at 00:44
  • I don't think it's an intentional feature to allow completely off the wall code in `tsconfig`. That said, I am not on the TypeScript team. – Shaun Luttin May 15 '19 at 02:57

1 Answers1

1

How to use JSZip in an old-fashion ambient project with concatenated JavaScript files

From the official documentation, JSZip can be installed manually:

Manually : download JSZip and include the file dist/jszip.js or dist/jszip.min.js

For TypeScript, the easiest way is without typings. Create a new file with this content:

// global-defs.d.ts
declare const JSZip: any;

Or, you can try to import typings manually. Create a file jszip.d.ts and paste all the content of its typings. Now, remove the last line export = JSZip;. Your file ends with:

declare var JSZip: JSZip;

Then, the code from the package documentation should works.

The classic solution for a brand new project

In a clean directory:

npm init
npm install --save jszip
npm install -D @types/jszip typescript

Then, create a valid tsconfig.json file. For example:

{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
  },
  "exclude": [
    "node_modules"
  ]
}

Create a source file:

// main.ts
import JSZip = require('jszip');

// Here, use the code from the package documentation
// here: https://www.npmjs.com/package/jszip

Some advice:

  • Do not use import * as JSZip for something that is not a module namespace object. You should use import JSZip = require(…) in your case.
  • Do not use TypeScript namespaces and ES6 modules together. Your code imports a module, so just remove your namespace program.

More documentation on ES6 modules (including module namespace objects) here.

Paleo
  • 21,831
  • 4
  • 65
  • 76
  • Thanks for the advice. That does indeed work in a vacuum, but I am modifying an existing and decently large codebase with "namespace program {" at the start of each file and several other namespaces declared besides. I do not think removing use of namespaces is the appropriate solution in my case. Is there a way to not completely break the architecture of this existing codebase and still use JSZip? At any rate I am starting to do just that, but I may be cowed by the large workload it implies. It strikes me as a bit absurd to require this amount of disruption for use of one library. – Empty Set May 15 '19 at 00:27
  • Just an update - removed all uses of "namespace" from my code. I saw many errors with inter-module references and started using "import {...} from "./x.ts"" syntax where needed. I also had to modify my compiler options as my usage of a Web Audio reference mysteriously broke, too. In the end the "require" forced me to use the "--module amd" compiler tag which meant I had to move over to requirejs. And in the end the ts would compile without error but the project would fail to initialize. No errors on web console. I may have to revert at this point (I have it under revision control thankfully). – Empty Set May 15 '19 at 02:23
  • @EmptySet The existing code base does not use import or export at all? – Paleo May 15 '19 at 05:44
  • Some classes/functions are "export" but there are no explicit "import"s (instead triple-slash references to files in the same namespace are used). Removing the namespaces without changing anything else caused errors in this architecture though I can't remember exactly what at the moment. It may have been "Cannot use output in module without --module set to "system" or "amd"", the direct fix for which I wanted to eschew for a while. This led me down the road of changing architecture. I will share the exact errors when I have a chance to revert and look at it again. – Empty Set May 15 '19 at 13:13
  • @EmptySet, the exported functions and classes are never top-level? All are in the namespace? – Paleo May 15 '19 at 14:27
  • @EmptySet I edited to add a solution without typings. Maybe it's possible to still add ambient typings but I'm unsure. – Paleo May 15 '19 at 14:44
  • Yes, the exports are from within the namespace only. There are also two or three other smaller namespaces that are referenced from files in the main namespace with something like "const { class1, class2 } = otherNamespace;". It sounds convoluted but it is rather clean organizationally (one is an HTML object creation helper namespace that declares "div" and "svgElement" for example). I will try the typeless solution later as it might be enough to use the library without the heavy modification I described. – Empty Set May 15 '19 at 15:14
  • 1
    Okay, looks like that simple route worked without modification of the codebase (the upper suggestion in your reply). I am surprised it was that straightforward. Thanks! – Empty Set May 16 '19 at 01:52