Typescript import mechanism in Nodejs - node.js

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.

Related

Typescript generates properties which not exists [duplicate]

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

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...

Unable to import lodash

I'm new to TypeScript and I'm facing a problem while trying to load lodash.
Here is my code :
///<reference path="../../typings/lodash/lodash.d.ts"/>
///<reference path="../interfaces/IScheduler.ts"/>
import _ = require('lodash');
module MyModule {
export class LeastUsedScheduler implements IScheduler {
/// CODE HERE
}
}
I tried replacing the import line by :
import * as _ from lodash;
In the two cases I get :
"error| Cannot find name 'IScheduler'."
When I remove the import directive it compiles perfectly but _ is undefined at runtime.
I also tried to put the import inside the module without success.
I'm sorry It must be a very dumb question but I can't figure it out.
Thank you
EDIT :
I understood the problem. Referencing the typing for lodash created the variable _ in the scope. That's why it compiles fine without the import line. The problem is referencing the typing does not really import lodash. That's why it fails at runtime.
When I import lodash the compilation fails because lodash is already in the scope.
Thank you for your support.
I'm not 100% about the issue but can you try the following and let me know how it goes?
///<reference path="../../typings/lodash/lodash.d.ts"/>
///<reference path="../interfaces/IScheduler.ts"/>
import _ = require("lodash");
export class LeastUsedScheduler implements IScheduler {
doSomething(){
_.each([],function name(parameter) {
// ...
});
}
}
When compiled it looks like:
var _ = require("lodash");
var LeastUsedScheduler = (function () {
function LeastUsedScheduler() {
}
LeastUsedScheduler.prototype.doSomething = function () {
_.each([], function name(parameter) {
throw new Error("Not implemented yet");
});
};
return LeastUsedScheduler;
})();
exports.LeastUsedScheduler = LeastUsedScheduler;
If you import the module import _ = require("lodash"); but you don't use it, TypeScript will remove the import (I added the doSoemthing method for that reason).
Update: Why it didn't work?
The problem was that the module keyword is used to declare an internal module. At the same time the code was loading an external module. You should avoid mixing internal and external modules. You can learn more about the difference between internal and external modules at http://www.codebelt.com/typescript/typescript-internal-and-external-modules/.
Also, if you use internal modules avoid using the module keyword as it is deprecated and you should use the namespace keyword instead.

How do you write a node module using typescript?

So, the general answer for the other question (How do you import a module using typescript) is:
1) Create a blah.d.ts definition file.
2) Use:
/// <reference path="./defs/foo/foo.d.ts"/>
import foo = require("foo");
Critically, you need both the files foo.d.ts and a foo.js somewhere in your node_modules to load; and the NAME foo must exactly match for both. Now...
The question I would like to have answered is how to you write a typescript module that you can import that way?
Lets say I have a module like this:
- xq/
- xq/defs/Q.d.ts
- xq/index.ts
- xq/base.ts
- xq/thing.ts
I want to export the module 'xq' with the classes 'Base' from base.ts, and 'Thing' from thing.ts.
If this was a node module in javascript, my index.ts would look something like:
var base = require('./base');
var thing = require('./thing');
module.exports = {
Base: base.Base,
Thing: thing.Thing
};
Let's try using a similar typescript file:
import base = require('./base');
export module xq {
export var base = base.Base;
}
Invoke it:
tsc base.ts index.ts things.ts ... --sourcemap --declaration --target ES3
--module commonjs --outDir dist/xq
What happens? Well, we get our base.d.ts:
export declare class Base<T> {
...
}
and the thrillingly unuseful index.d.ts:
export declare module xq {
var Base: any; // No type hinting! Great. :(
}
and completely invalid javascript that doesn't event import the module:
(function (xq) {
xq.base = xq.base.Base;
})(exports.xq || (exports.xq = {}));
var xq = exports.xq;
I've tried a pile of variations on the theme and the only thing I can get to work is:
declare var require;
var base = require('./base');
export module xq {
export var base = base.Base;
}
...but that obviously completely destroys the type checker.
So.
Typescript is great, but this module stuff completely sucks.
1) Is it possible to do with the built in definition generator (I'm dubious)
2) How do you do it by hand? I've seen import statements in .d.ts files, which I presume means someone has figured out how to do this; how do those work? How do you do the typescript for a module that has a declaration with an import statement in it?
(eg. I suspect the correct way to do a module declaration is:
/// <reference path="base.d.ts" />
declare module "xq" {
import base = require('./base');
module xq {
// Some how export the symbol base.Base as Base here
}
export = xq;
}
...but I have no idea what the typescript to go along that would be).
For JavaScript :
var base = require('./base');
var thing = require('./thing');
module.exports = {
Base: base.Base,
Thing: thing.Thing
};
TypeScript :
import base = require('./base');
import thing = require('./thing');
var toExport = {
Base: base.Base,
Thing: thing.Thing
};
export = toExport;
Or even this typescript:
import base = require('./base');
import thing = require('./thing');
export var Base = base.Base;
export var Thing = thing.Thin;
Typescript has really improved since this question was asked. In recent versions of Typescript, the language has become a much more strict superset of Javascript.
The right way to import/export modules is now the new ES6 Module syntax:
myLib.ts
export function myFunc() {
return 'test'
}
package.json
{
"name": "myLib",
"main": "myLib.js",
"typings": "myLib.d.ts"
}
Dependents can then import your module using the new ES6 syntax:
dependent.ts
import { myFunc } from 'myLib'
console.log(myFunc())
// => 'test'
For a full example of a node module written in Typescript, please check out this boilerplate:
https://github.com/bitjson/typescript-starter/

How to export and import type definitions?

I've been using TypeScript for a while and the module system continues to be a mystery to me.
I have this type definition file (appComponents.d.ts):
/// <reference path="./authentication/API.d.ts"/>
import express = require('express');
declare module appComponents {
export interface IComponents {
application: express.Application;
authenticationService: MyApp.IAuthenticationService;
// and so on ...
}
}
and this file (index.ts):
/// <reference path="./appComponents.d.ts"/>
import express = require('express');
import mssql = require('mssql');
function initComponents(): appComponents.IComponents {
// Components initialized here ...
}
Two questions:
Why do I have to use
import express = require('express');
instead of
/// <reference path="./path/to/definitely-typed/express/express.d.ts"/>
to avoid error TS2095: Could not find symbol 'express'.? After all, this is just a type definition file that generates no JavaScript depending on the types in another type definition file that also generates no JavaScript.
Why does index.ts cause error TS2095: Could not find symbol 'appComponents'.? And when I do this instead:
import appComponents = require('./appComponents');
why does it cause error TS2094: The property 'IComponents' does not exist on the value of type 'appComponents'.?
Using TypeScript 0.9.7.0.
) Why do I have to use
import express = require('express');
instead of
/// <reference path="./path/to/definitely-typed/express/express.d.ts"/>
Actually you need to use both:
/// <reference path="./path/to/definitely-typed/express/express.d.ts"/>
import express = require('express');
You probably have the reference in API.d.ts or simply have express.d.ts included in your visual studio project somewhere.
How it works : https://github.com/borisyankov/DefinitelyTyped/blob/master/express/express.d.ts#L15 contains declare module "express" { This tells typescript what to give (everything with export in this file : https://github.com/borisyankov/DefinitelyTyped/blob/master/express/express.d.ts when someone does import / require i.e import express = require('express') These are known as external modules in typescript and can be amd / commonjs
Why does index.ts cause error TS2095: Could not find symbol 'appComponents'
Because you are declaring an internal module and trying to import it as an external module.
PS: video about external / internal modules : http://www.youtube.com/watch?v=KDrWLMUY0R0&hd=1
UPDATE
You said that one of my modules in the question is an internal module:
Just FYI: declare module appComponents { makes appComponents an internal module. You should do declare module "appComponents" { if you want to declare an external module and use import appComponents = require('appComponents'); But don't. Its not what you want.
Why does index.ts cause error TS2095: Could not find symbol 'appComponents'.?
Because appComponents.d.ts does an import it too became an external module. You should move declare module appComponents { etc. to its own file free of external modules and then use ///<reference
Here's what solved my problem (#2):
Don't create .d.ts files as a way of separating interface and implementation of classes. Instead, define interfaces in .ts files. When a class and the interface it implements are in different files, require the interface module from the class module. Do this also for interfaces that are not to be implemented by a class, i.e. interfaces used for strong typing of variables.
Example:
// IAppComponents.ts
/// <reference path="../DefinitelyTyped/express/express.d.ts"/>
import express = require('express');
import IAuthenticationService = require('./services/IAuthenticationService')
interface IAppComponents {
application: express.Application;
authenticationService: IAuthenticationService;
}
export = IAppComponents;
// IAuthenticationService.ts
import ILoginCallback = require('./ILoginCallback');
import ILogoutCallback = require('./ILogoutCallback');
interface IAuthenticationService {
login(user: string, pass: string, callback: ILoginCallback) void;
logout(sessionToken: string, callback: ILogoutCallback): void;
}
export = IAuthenticationService;
// AuthenticationService.ts
import IAuthenticationService = require('./IAuthenticationService');
import ILoginCallback = require('./ILoginCallback');
import ILogoutCallback = require('./ILogoutCallback');
// We can't import this, because it's a js module with no matching type definition.
var mssql = require('mssql');
class AuthenticationService implements IAuthenticationService {
login(user: string, pass: string, callback: ILoginCallback): void {
// Implementation goes here ...
}
logout(sessionToken: string, callback: ILogoutCallback): void {
// Implementation goes here ...
}
}
export = AuthenticationService;
This approach works, with one tiny caveat: The TypeScript compiler generates .js files for each .ts (but not .d.ts) file, even if the .ts file contains only interfaces, resulting in empty .js files being generated. I can live with that for now, but I hope the TypeScript team does something about it one day.

Resources