rschristian
rschristian

Reputation: 2956

Typescript ignore string not assignable to type in JS

I'm writing JS with a jsconfig.json in order to provide extra TS type checking.

The issue is that the TS language server doesn't seem to be smart enough or equipped to handle such situations. I get the following error:

Types of property 'mode' are incompatible.
    Type 'string' is not assignable to type '"production" | "development"'.

for the following code:

return {
  ...
  mode: isProd ? 'production' : 'development',
}

I completely understand why this is an error and how to solve it in TS (see https://stackoverflow.com/a/37978675/15388164), but in JS, we don't have type or const assertions.

Is there a good way to deal with this? I use this config in half a dozen spots, so I don't want to //@ts-ignore every site. If anything, I'd like to disable these 'string is not assignable to other strings' issues altogether as they provide no value in JS as far as I can see. The types will always be inferred incorrectly.

Upvotes: 1

Views: 3029

Answers (1)

jered
jered

Reputation: 11571

Weird - what version of TypeScript are you using? TypeScript is generally pretty good about inferring types, even from vanilla JS files. You might have some kind tsconfig error somewhere - maybe post more of your config info so we could have a look.

Another option is to use JSDoc annotations in your JS files. JSDoc is pretty awesome for this purpose and plays very nicely with TypeScript. You can use an inline @type annotation to directly define the type for a specific field of your return object [Edit: better solution below this one]:

return {
    /**
     * @type {"production" | "development"}
     */
    mode: isProd ? "production" : "development",
}

Edit: after playing around in codesandbox.io a bit, it seems that TypeScript does not infer object property types very well - it generally works best when inferring basic variable values and function returns etc. (This makes sense if you think about it - an object property value can easily change to something else and can not be assumed to be constant, whereas a basic variable declaration that is declared as const foo = "myString" is pretty much guaranteed to always be "myString")

Probably a better option than the one above would be to import the Webpack configuration type into your JS files via JSDoc and use it to type your config object directly. Something like this:

/**
 * @param {boolean} isProd
 * @returns {import('webpack').Configuration}
 */
export function getConfig(isProd) {
  return {
    mode: isProd ? "production" : "development"
  };
}

This has the awesome benefit of TypeScript actually checking the proper types of your Webpack config fields directly in your vanilla JavaScript file, in addition to playing nicely when you utilize that config object in TypeScript files.

Upvotes: 2

Related Questions