Why typescript recognizes imported variables as of type any?

I have the following 2 files:

File src/helpers.ts:

const someHelperArray = ['value1', 'value2'] as const

module.exports = {
  someHelperArray
}

File src/car.ts:

const {
  someHelperArray
} = require('./helpers')

In the file car.ts when I hover over the someHelperArray I get this typescript type resolution: const someHelperArray: any instead of literal type I expected ('value1' | 'value2'). Essentially typescript doesn’t recognize the types of imported variables from another file. I tried changing tsconfig.json settings but nothing helped. How to can I get typescript to recognize types imported from other files?

This is my tsconfig.ts:

{
    "compilerOptions": {
      "lib": ["dom", "es6", "scripthost", "esnext"],
      "moduleResolution": "node",
      "baseUrl": "src",
      "watch": true,
      "allowJs": true,
      "esModuleInterop": true,
      "module": "commonjs",
      "sourceMap": true,
      "inlineSources": true,
      "allowSyntheticDefaultImports": true,
      "noUnusedLocals": true,
      "noUnusedParameters": true,
      "noImplicitAny": true,
      "strictNullChecks": true,
      "resolveJsonModule": true,
      "experimentalDecorators": true
    },
    "exclude": ["node_modules", "**/*.spec.ts", "ts-out/**/*", "babel-out/**/*"]
  }
  

Answer

A CommonJS module (with require) is not statically analyzable, which means the content of module is unknown before run time. Indeed, you could write any kind of dynamic code to assign it (i.e: Object.assign). So if you need to retain the types between modules you have to write them as ES6 modules because these are statically analyzable. Note that it is supported in latest versions of Node.js only.

If you wanted to keep using CommonJS modules in the source code, you could also have one file src/helpers.js:

const someHelperArray = ['value1', 'value2'];

module.exports = {
  someHelperArray
}

And one src/helpers.d.ts like this:

export const someHelperArray: ['value1', 'value2'];