Nitzan Tomer Nitzan Tomer - 24 days ago 7
TypeScript Question

importing goes wrong with systemjs

I'm trying to use an existing js library (validate.js) in both the client and server.

I installed it using npm, and everything compiles for both server and client.

When using it in the server it works great, but when I execute it in the browser it throws an error.

The same file is used in both cases:

import validate = require("validate.js");

export function RequestValidator(data: any): any {
return (validate as any)(data, constraints, { allowEmpty: true });
}


validate
is asserted to
any
becuase otherwise I get:


TS2349: Cannot invoke an expression whose type lacks a call signature.


The
.d.ts
I'm using is:

declare module "validate.js" {
export interface ValidateJS {
(attributes: any, constraints: any, options?: any): any;
async(attributes: any, constraints: any, options?: any): Promise<any>;
single(value: any, constraints: any, options?: any): any;
}

export const validate: ValidateJS;
export default validate;
}


The module only exports a function, and that works well in the server, but in the client when invoking this function I get:

Uncaught TypeError: validate is not a function(…)


The code is compiled using target
commonjs
for the server:

"use strict";
const validate = require("validate.js");
...


And
system
for the client:

System.register(["validate.js"], function(exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
var validate;
...

return {
setters:[
function (validate_1) {
validate = validate_1;
}],
...


When debugging it,
validate
indeed isn't a function it's:

validate: r
EMPTY_STRING_REGEXP: (...)
get EMPTY_STRING_REGEXP: function()
set EMPTY_STRING_REGEXP: function()
Promise: (...)
get Promise: function()
set Promise: function()
__useDefault: (...)
get __useDefault: function()
set __useDefault: function()
async: (...)
get async: function()
set async: function()
capitalize: (...)
get capitalize: function()
set capitalize: function()
cleanAttributes: (...)
get cleanAttributes: function()
set cleanAttributes: function()
...


Any idea what's going on and why it behaves this way in the browser?

Answer

When you compile with "module": "system", node-compatible import

import validate = require("validate.js");

no longer works - the value you are getting for validate is a module, not a function. This may be a bug in typescript, or it could be by design - I don't know. (update: from github comment addressed to JsonFreeman here, it looks like it's by design: you get the module object with a set of properties including one named default).

There are several ways around this.

First, you can do easy conversion yourself - the function that you need is provided as default property of the module, so this line will fix it:

validate = validate.default ? validate.default : validate;

Or, you can compile with "module": "commonjs" even for the browser, so typescript will generate working code, and systemjs will detect format for your modules automatically.

Or finally, you could still compile with "module": "system", but import validate.js as it's intended in its typings:

import validate from 'validate.js';

That way you don't have to do any casts to any, and typescript will generate necessary access for default property, but the drawback is that it will not work at all in node when imported that way.

Comments