I'm working on a node.js project that for historical reasons involves both TypeScript and JavaScript. I'd like to set up some project-wide constants that can be used in both languages. Obvious: write a .js
file with the constants, and a .d.ts
file that includes the types for TypeScript's use. But no: several hours of beating on the problem (and RTFMing) have gotten me nowhere.
Here's my first try at the constants file:
// constants.js
const MY_CONST_1 = 1;
exports.MY_CONST_1 = MY_CONST_1;
... and the JavaScript which uses it:
// consumer.js
let constants = require('./constants');
let one = constants.MY_CONST_1;
That works well, but when I try the obvious in TypeScript:
// consumer.ts
import * as constants from './constants';
let one: number = constants.MY_CONST_1;
Nope: error TS2307: Cannot find module './constants'.
So, gotta write a constants.d.ts
file, in the same directory as the constants.js
file, right? Sounds easy, but a half dozen different attempts have yielded exactly zilch. No matter what I do, Typescript complains error TS2306: File '/Users/griscom/Documents/Work/test/constants.d.ts' is not a module
.
So, what would a successful constants.d.ts
file look like? Or, if I'd have to make changes in constants.js
for it all to work, what would they be?
P.S. Here's my tsconfig.json
file:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"noImplicitReturns": true
}
}
(This seems like such a simple and obvious need; it's frustrating how opaque the process is.)
allowJs
Try using the compiler option allowJs: true
. This will allow your original import from ./constants
to work even though it's a .js
file, without the need to create a separate .d.ts
file. TypeScript will understand inferrable types from the .js
file, for example you will get type checking against MY_CONST_1
as a number.
As you said in your comment this requires separating your source files from your output files, because it can't output a JS file at the same location as a JS source file.
constants.d.ts
declaration fileGiven your source file that looks like this:
// constants.js
export const MY_CONST_1 = 1;
You can write a constants.d.ts
file that looks like this:
// constants.d.ts
export const MY_CONST_1: number;
Where you were off in your previous attempts is using the declaration module
, which is used to declare types for external modules (like from node_modules
). In this case simply putting the constants.d.ts
file in the same directory as constants.js
and using export
will tell TS that it's a JS module that exports that value:
import { MY_CONST_1 } from "./constants";
import * as constants from "./constants";
Something important to keep in mind is that the .d.ts
file is not checked against the .js
file to ensure it's actually correct, that's on you. So if you make a change to the .js
file you have to make sure to update the .d.ts
file accordingly and correctly. A compile error or lack of compile error does not mean it's correct or not. This is one reason allowJs
exists because it avoids such leaks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With