Daniel Griscom
Daniel Griscom

Reputation: 2253

Writing .d.ts file so I can share constants with JavaScript and TypeScript?

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

Upvotes: 4

Views: 12530

Answers (3)

Romain Deneau
Romain Deneau

Reputation: 3061

Another option: using an import for side effects only. Indeed, your constants are global, i.e. not defined in a module due to the missing export keyword:

import './constants';

To be used in combination with the allowJs compiler option mentionned in an another response.

Upvotes: 0

Aaron Beall
Aaron Beall

Reputation: 52153

Option 1: 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.

Option 2: constants.d.ts declaration file

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

Upvotes: 9

JGoodgive
JGoodgive

Reputation: 1126

export const MyConst =1;

Or

export class MyGroup {
    static MyConst: number
}

Generally stay clear of modules since that isnt compatible with the meaning of modules in inports. Instead use "namespace" which is very similar to a class with static members. Meaning you could probably do (on phone and cant try it out):

export namespace MyNamespace {
    export const MyConst = 1;
}

But lastly try avoid wrapping things in extra namespaces and classes etc since the import syntax is the module. Also it makes your code treeshakeable.

Upvotes: 0

Related Questions