Typescript generates properties which not exists [duplicate] - node.js

I have an external library thing.d.ts file with a global definition inside:
declare var thing: ThingStatic;
export default thing;
I reference npm module in my TypeScript:
import thing from 'thing';
...
thing.functionOnThing();
When I transpile the TS (targeting ES6) it looks something like this:
const thing_1 = require("thing");
...
thing_1.default.functionOnThing();
This then throws an error:
Cannot read property 'functionOnThing' of undefined
Why is TypeScript adding .default between thing_1 and functionOnThing()?
There is no property named default on ThingStatic, and no default property on the underlying JS object that the .d.ts file defines.
Why is TypeScript adding the property and how do I stop it?

import thing from 'thing';
This line of code means "import the default export from the module 'thing' and bind it to the local name thing".
TypeScript does as you requested and accesses the default property of the module object.
What you probably meant to write was
import * as thing from 'thing';

This appears to be a bug with global TS definitions and "module": "commonjs" in the tsconfig.json.
You can either use global TS definitions and stitch all your output into a single file, or you can use modules and directly import them.
The error here is due to the require returning the module context, and the name of the default being irrelevant - it always becomes default...
declare var thing: ThingStatic;
export thing; // Explicit export for thing
export default thing; // Default export for thing
Now require will return this context, so with commonjs modules:
import module from 'thing';
var thing = module.default; // From the default export
var alsoThing = module.thing; // From the named export
However, I've found this to be inconsistent, so switched to es6 modules:
import thing from './thing'; // Import default
import { thing } from './thing'; // Import named
const thing = (await import('path/to/thing.js')).default; // Import dynamic

Related

What does Babel do to let me use any name to import a node module that exports a function

An example will make my question clearer, say I want to import debug module to my vuejs codes
Debug module export createDebug function like this,
module.exports = require('./browser.js');
...
exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
function createDebug(namespace) { ... }
When I use import to import debug module, I can give it any name I want, like
import debug from 'debug' // or any name I want, e.g
import debugjs from 'debug'
I understand if export default anonymous function I can then import it with any name I want, but this is not the case here.
So why can I use any name to import it?
---------------- update -----------------
One takeaway from the answer is that import "any name" work for both default export anonymous function and named function.
but this is not the case here.
It kind of is. You are trying to import a module that has no ES6 exports. You are trying to import a CommonJS module. So Babel has to make a decision how to handle that case.
There are two common ways to export something from a CommonJS module:
Assign a property to exports (or module.expoets), for example exports.answer = 42;
Overwrite the value of module.exports, e.g. module.exports = 42;.
In the second case you end up exporting only a single value from the module. That's basically what a default export is (since there can only be one) and that's what you are doing in your code.
So in other words, when importing a CommonJS module via ES6 import statements, then the value of module.exports is used as the default export value.
We can confirm that by looking at how Babel converts the code:
// import foo from 'bar'; becomes
var _bar = require('bar');
var _bar2 = _interopRequireDefault(_bar);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
// ^^^^^^^^^^^^^^^^
}

Pass on all exports from a module object

I've got the following js file:
// db.js
export default /* ... */;
export const tableName = /* ... */;
And the following mock for it (using jest):
const MockDb = jest.genMockFromModule('../db.js');
export default MockDb.default;
export const { tableName } = MockDb;
Now, if I add an export to my original db.js file, I have to also touch the mock accordingly. I'd rather have the mock file just automatically export all exports from the MockDb object (which is a module object generated by jest holding a default key and other named properties for each export).
According to MDN, the export * from X
syntax only works, when X is a string referring to a module. Can I somehow export * from <moduleObject>?
Thanks!
Can I somehow export * from <moduleObject>?
No, you can't. The export names must be statically declared, they can't be determined at runtime from an object.
The correct approach would a module loader plugin which automatically calls Jest when you do something like import … from '../db.js!jest';.

Can I use a custom module resolution function (like "proxyquire") in place of require() with TypeScript?

I have a TypeScript file config.ts that will be run with node:
import myDependency = require('my-dependency');
export = {
doSomething = () => {
...
}
}
In other TypeScript file, I can import this file with full type safety:
import config = require('./config');
config.doSomething();
config.doSomethingElse(); // compiler error, this method doesn't exist
Now I want to unit test this script. In order to mock out the dependencies that this script require()s I'm using proxyquire, which lets me provide the values that my script will get when it makes calls to require(). Here's what my test might look like:
import proxyquire = require('proxyquire');
const config = proxyquire('./config', {
'my-dependency': {} // this mocked object will be provided when config.ts asks for `my-dependency`
});
expect(config.doSomething()).to.do.something();
This works fine, except that my config variable is of type any because I'm using proxyquire() in place of require(). TypeScript must give the require() function special treatment to allow it to perform module resolution. Is there a way to tell the TypeScript compiler that proxyquire() should also do module resolution, similar to require()?
I could rewrite config.ts as a class or make it use an interface. Then I would be able to explicitly type the variables in my tests by importing the class/interface definition. But allowing proxyquire() to implicitly type things for me would be far be the easier solution.
There is a workaround - you can get the type of config.ts module by importing actual module and using typeof in a type cast:
import proxyquire = require('proxyquire');
import configType = require('./config');
const config = <typeof configType> proxyquire('./config', {
'my-dependency': {} // this mocked object will be provided when config.ts asks for `my-dependency`
});
config.doSomething();
// config.noSuchMethod(); // does not compile
This is not ideal because you have to import the same module in your test twice - the real one just to get at the type of it and "proxiquired" one to actually use in your tests, and you have to be careful not to mix up the two. But it's pretty simple compared to the task of implementing another variant of module resolution for typescript. Also, when configType is used in this way - for typing only - its import will not even appear in generated javacsript code.

TypeScript External Node Module Declaration File For Typings

I have a Node.js module (index.js) that has been transpiled with Babel to the code as follows:
// My stuff ...
var fs = require("fs");
var path = require("path");
exports.fileContent = fs.readFileSync(
path.join(__dirname, "file.txt"),
'utf8'
);
// Babel compiler stuff ...
Object.defineProperty(
exports,
"__esModule",
{ value: true }
);
// My stuff again ...
exports.default = exports.fileContent;
Usage in Node.js would be:
var myModule = require("my-module");
doSomethingWithIt( myModule.fileContent );
As far as I understand, I need to create a .d.ts declaration file and reference it in the typings field in my package.json. And also, this declaration file has to be a module. So my first approach after reading several tutorials about this topic was:
// index.d.ts
declare module "my-module" {
export const fileContent: string;
export default fileContent;
}
But sadly, this was a fail:
[...] error TS2656: Exported external package typings file 'index.d.ts' is not a module. [...]
My next approach was to get rid of the declare module thing:
export const fileContent: string;
export default fileContent;
This works for me, but however feels wrong as I did not find any .d.ts example file that is not using the declare stuff. I also noticed that I should not use export default in a namespace/module, which leads me to the point where I don’t understand how to declare the default export of my module at all.
Here are my questions:
How do I do this the right way?
How can I make sure that TypeScript recognizes the default property?
Do I need to use the declare module stuff?
Do I need to declare the imports (fs and path)?
Edit:
After a bit more researching and fiddling, I think I have found the solution by myself. As far as I understand, the external module description/declaration has to be a module—which means that it has to import or export something: In this case, it has to export the declaration and the declared constant as the default export as well:
export declare const fileContent: string;
export default fileContent;
error TS2656: ... is not a module happens when trying to use ES6 style imports and the definition does not export a module.
I suggest changing your definition to
declare module MyModule {
export const fileContent: string;
}
declare module "my-module" {
export default MyModule
}
Since this definition is now "default exporting" a module, it can be imported using the default ES6 import syntax
import MyModule from "my-module"
Then use it
MyModule.fileContent...

Typescript import mechanism in Nodejs

I'm totally stuck right now. Using Nodejs.
Having the following setup:
Compile -target ES5 --module commonjs
/def/mongoose.d.ts:
export = M;
declare module M {
export class Collection {
name:string;
}
}
/model/users.ts:
///<reference path='..\def/mongoose.d.ts' />
export var foo:M.Collection;
Error: /model/users.ts(21,16): error TS2095: Could not find symbol 'M'.
Made it as simple as possible. I tried a lot but did not managed to access the class in the mongoose.d.ts
Instead of using a reference comment, you should import the module:
import M = require('./def/mongoose');
export var foo: M.Collection;
Usually, you would give the .d.ts file the same name (and location) as the .js file so the import statement would also load it at runtime.

Resources