Reputation: 683
There is a well-known approach in node.js to leverage the module factory pattern. For, example:
m.js
function factoryMethod(module) {
// doing some stuff and returning object initialized with module parameter
}
app.js
var o = require('./m')(module);
// using o
How can I do the same in typescript. Actually, creating m.ts is not a problem:
m.ts
function factoryMethod(module: NodeModule): any {
// doing some stuff
}
export = factoryMethod;
But how should I use import syntax to use this module factory method like in javascript?
Upvotes: 1
Views: 1471
Reputation: 3345
I know this question is fairly old but I came across the same issue when converting an existing JS project to TypeScript that makes use of this pattern.
Instead of having to pre-define the module definition in an interface we can use the ReturnType
helper to get a type definition of the return type of a function!
This is what the module might look like:
function initModule(arg1: number, arg2: number) {
function functionA() {
return arg1;
}
function functionB() {
return arg2;
}
return {
functionA,
functionB,
};
}
export default initModule;
export type SampleModule = ReturnType<typeof initFactory>;
And here is how we could use it:
import initModule, { SampleModule } from './factory';
const newModule: SampleModule = initModule(1, 2);
newModule.functionA(); // 1
newModule.functionB(); // 2
Seriously, how cool is TypeScript? :)
Upvotes: 0
Reputation: 7188
I'm not sure I quite get this common pattern. You're passing the module
object from one module into another? All kinds of other objects are passed around like this, (e.g. app
, db
), but I don't like the idea of passing around the module
object. I'd be tempted to call it an anti-pattern. Surely the module
object should stay in the module to which it belongs.
That said, if you just want to import and call a function on the same line, you could do this using Node's require()
function, just like regular JavaScript. Let's say you are passing an Express app
rather than module
.
const o = require('./m')(app);
However, you lose type-safety with this; o
will be of type any
. You would have to explicitly define the type of o
.
const o: Module = require('./m')(app);
This is a bit silly. In fact, Module
is likely to be defined in the module you are requiring anyway, so it is likely also self-defeating. My suggestion is this. Don't expect to use the same patterns you are used to in plain JS in TypeScript, which has its own patterns.
One thing you could do is import the function at the top, and then call it later. TypeScript uses ES2015-style modules, which don't allow you to import a function and call it on the same line. You will have to rewrite both files, since export =
is not valid in ES2015.
// m.ts
interface Module {
// properties, methods, etc.
}
export function factoryMethod(app: Express.Application): Module {
let module = {};
// Initialize module methods, properties, etc.
return module;
}
The interface allow type inference in app.ts
, which is an improvement of a kind.
// app.ts
import {factoryMethod} from './m';
// ...
let o = factoryMethod(app);
But this is still very silly. We don't need to define an interface and all that nonsense. Instead, we can use a class. Classes are very often the answer in TypeScript, and you'll find that most patterns in TypeScript involve them.
// m.ts
export class Module {
constructor(private app: Express.Application) { }
cache: string[];
someMethod(): Promise<Express.Response> {
// do something with this.app
}
}
And then in app.ts
import {Module} from './m';
// ...
let o = new Module(app);
Now we don't have to worry about interfaces and all that. The class itself is a type. This is quite a bit different from what you are likely to see in a typical Node app, but it is the sort of pattern you find all the time in TypeScript.
Hopefully that gives you some ideas.
Upvotes: 1
Reputation: 6803
import {factoryMethod} from './m.ts'
let module = factpryMethod('module');
Upvotes: 1