1

Today I was writing a type declaration file for a JavaScript file but despite my hours of trying I couldn't make it work inside a node application. Then I tried switching to es6 module syntax and surprisingly it worked.

Then I discovered that I can also make it work with commonjs module syntax if I add ".default" before accessing any properties of that module. For example I've to use person.default.name instead of person.name to get the intellisence.

I wrote a small module to demonstrate the problem. I've created an object with identical typings of my actual module.

index.js

const names = {
  firstname: "Alex",
  middlename: "Blex",
  lastname: "Clex",
};

const state = {
  isAlive() {
    return true;
  },
  isWalking() {
    return false;
  },
};

function talk(speech) {
  console.log(speech);
}

const person = {
  names,
  state,
  talk
};

module.exports = person;

index.d.ts

declare type predicate = (v: unknown) => boolean;
declare function talk(speech: string): void;

declare const person: {
  names: {
    [key: string]: string;
  };
  state: {
    [key: string]: predicate;
  };
  talk: typeof talk;
};

export default person;

test1.js

const person = require("./index");

const isAlive = person.default.state.isAlive();
//---------------------^^^^^^^----------------
// The above line throws an error as expected.
// I've to use "person.default.whatever" to get intellisence
// In the editor it shows "typeof isAlive = boolean".

const isReallyAlive = person.state.isAlive();
// EditorError: Property 'state' does not exist on type
// 'typeof import(./index.js)'.

// In the editor it shows typeof "isReallyAlive = any"
// But infact isReallyAlive is boolean.

test2.js

Using es6 module syntax it works perfectly.

Using es6 module syntax

I'm fairly new to Typescript so kindly give me some hint where I'm making the mistake. Thanks in advance, I highly appreciate your time on StackOverflow <3.

9
  • What about just using person.state? default is normally only used for ESM not CommonJS Commented Sep 24, 2021 at 15:04
  • With commonjs module the editor says person.state is not defined but if I console.log person.state it indeed prints the state object. Commented Sep 24, 2021 at 15:15
  • I'm assuming the real file is much more complicated than that, because otherwise it would be really simple to just convert it to ts lol Commented Sep 24, 2021 at 15:42
  • If I do const person = require("./index"); person is any for me. Do you have any special tsconfig? I used a fresh project with npx tsc --init. Commented Sep 24, 2021 at 15:50
  • 1
    Let us continue this discussion in chat. Commented Sep 24, 2021 at 16:03

1 Answer 1

1

So as explained by this post https://stackoverflow.com/a/40295288/10315665 the export default option only creates a default key inside of the exports object. That's why you can still have normal exports besides it.

Many people consider module.exports = ... to be equivalent to export default ... and exports.foo ... to be equivalent to export const foo = .... That's not quite true though, or at least not how Babel does it.

So your definition file is wrong. It should be:

declare type predicate = (v: unknown) => boolean;

export declare const names: {
    [key: string]: string;
};

export declare const state: {
    [key: string]: predicate;
};

export declare function talk(speech: string): void;

And if you respect that, you can actually utilize typescript and it's awesome type checking by simply writing typescript in the first place!:

export const names = {
  firstname: "Alex",
  middlename: "Blex",
  lastname: "Clex",
};

export const state = {
  isAlive() {
    return true;
  },
  isWalking() {
    return false;
  },
};

export function talk(speech: string) {
  console.log(speech);
}

Just remember to enable "declaration": true in the tsconfig to also generate declaration files 😉.

Sign up to request clarification or add additional context in comments.

9 Comments

I just sort of "re-learnt" this, so yay for me :D
Sorry for late reply. I tried your solution with both nvim and vscode but sadly it didn't work. I still have to use .default. But I don't wanna waste your time anymore for this question. Lemme switch back to typescript. Thank you so much for giving me your valuable time <3 .
@h-sifat If you use the definition file I've provided it should work. Have you tried running npx tsc --noemit instead of just checking the editor? That should give you the real deal. Also, try the > Typescript: Restart TS Server command in vs code to clear any caching issues.
And you are not wasting my time, if I didn't feel like answering questions I would simply not open this website😉.
if you are trying to import this from typescript now, you would have to do import * as person from "./person";
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.