Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mixing JavaScript and TypeScript in Node.js

While having parts of a Node.js in plain ES6, is it possible to mix in some Typescript modules within the same project?

E.g. having some Types defined in TypeScript that are imported via require into plain ES6 files?

like image 366
Alexander Zeitler Avatar asked Apr 03 '18 22:04

Alexander Zeitler


People also ask

Does node JS use JavaScript or TypeScript?

Node. js packages are written in JavaScript and not Typescript. To get the type definitions for its packages, you need to install third-party packages called @types . This will give you access to type definitions for Express.


1 Answers

Yes this is possible.

Combine the following TypeScript compiler options

  1. --allowJs

    Explicitly supports mixed JavaScript and TypeScript sources

  2. --outDir

Since all files will be transpiled, it is necessary to output the resulting JavaScript into a different directory otherwise the input .js files would be overwritten1.

  1. --checkJs

This is completely optional. If specified, the compiler will typecheck JavaScript files, reporting errors just as in TypeScript files, where as it would otherwise tolerate inconsistencies.

As to using types declared in a TypeScript file in a JavaScript file, this can indeed be done.

TypeScript actually powers all of the JavaScript intellisense in tools like Visual Studio Code.

Types may be placed in JSDoc2 comments. These comments can reference types imported from TypeScript (.ts/.tsx/.d.ts) files. IDEs like Visual Studio Code will provide syntax-highlighting and auto completion within these comments.

There's a caveat however. Because there's no manifest syntax for types in JavaScript, they cannot be imported individually but must be attached to a value which is imported. This is most conveniently achieved via TypeScript's declaration merging as shown below.

Example:

a.ts

export default createThing;  function createThing(...args): createThing.Thing  {...}  namespace createThing {   export interface Thing {...} } 

b.js

import createThing from './a';  /**  * @param {createThing.Thing} thing  */ export function takesThing(thing) {} 

Notes:

1: --outDir is not necessary if you additionally specify the --noEmit flag. You would do this when using a tool such as SystemJS (with plugin-typescript) or Webpack (with ts-loader) to host the TypeScript transpiler. The same applies if you are using TS Node.

2: Although called JSDoc comments, they are interpreted in the context of the TypeScript type system, not the JSDoc system. Languages and tools like TypeScript, and Google's Closure Compiler, effectively hijack the JSDoc syntax for their own purposes and thereby confer potentially conflicting meanings to its constructs. This isn't usually a problem but it's worth knowing because it can be difficult to determine the applicability and correctness of these comments and the compatibility of the types that they reference or declare.

Remarks:

Although this question and answer is all about importing types for use in JavaScript files, it's very often unnecessary as the compiler will infer the types from the values of your expressions.

It's also worth mentioning that if you find yourself needing to write a lot of JSDoc style type annotations you are almost certainly better off converting the file to TypeScript as the syntax for expressing types in JSDoc is clumsy. Thanks to the --allowJs option, you can do this on a file by file basis, as described above.

like image 54
Aluan Haddad Avatar answered Sep 23 '22 10:09

Aluan Haddad