> ## Content Index
> Fetch the complete content index at: https://www.jonathancreamer.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Using Typescript in node.js scripts without actually writing Typescript
- URL: https://www.jonathancreamer.com/using-typescript-in-node-js-scripts-without-actually-writing-typescript/
- Published: 2024-11-08T18:33:22.000Z
- Updated: 2024-11-08T18:33:22.000Z
- Description: A few innovations both on the Typescript side, and the node.js side in the last few years have made it way easier to have your cake and eat it too when it comes to writing typesafe Javascript AND get to use modern ES modules!
- Author: Jonathan Creamer
- Tags: typescript, javascript, nodejs, jsdoc

Typescript is without a doubt my favorite way to write Javascript in 2024\. However, there are times when I don't want to actually have to run `tsc` or `tsc --watch` to do basic scripting tasks, but I also really want to have type safe Javascript, AND be able to use ES Modules.

A few innovations both on the Typescript side, and the node.js side in the last few years have made it way easier to have your cake and eat it too when it comes to writing typesafe Javascript AND getting to use modern ES modules instead of commonjs!

## Use jsdoc for the types

[JSDoc](https://jsdoc.app/?ref=jonathancreamer.com) has been around for quite a while now. I remember using it years ago to generate API docs when I built Backbone.js apps.

It's very similar in concept to what you'd use in C# apps as well, where you describe what a function or variable does by simply writing a code comment above it.

```javascript
/**
 * Debounces a given function
 * @param {function} func function to debounce
 * @param {number} time how long to debounce calls to func for (in milliseconds)
 * @returns { function } debounced function
 */
export function debounce(func, time) {
  // ...
}
```

You can use the `@type` to simply set a given JS var as a type.

```javascript
  export function debounce(func, time) {
    /** @type { NodeJS.Timeout | null } */
    let timeout = null;

    // ... 
  }
```

You can even define custom types using `@typedef`.

```javascript
/**
 * @typedef { Object } NpmrcOrg
 * @property { string } feed
 * @property { string } organization
 * @property { string } [pat]
 */

/** @type { Array<NpmrcOrg> } */
const defaultFeeds = [
  {
    organization: "foo",
    feed: "https://foo.dev.azure.com/foo/bar/_packaging/foo/npm/registry/",
  },
];
```

Using the square brackets marks a field as optional, both in `@property` and in `@param` like `[pat]` in the above example.

Here we're declaring a type called `NpmrcOrg` and then using it immediately below.

You can also reference types from other files, and even from NPM packages using `@import`

```javascript
/**
 * Create the user npmrc given an array of registry urls and a token
 * @param {object} options
 * @param {Array<import("../../utils/pat.js").NpmrcOrg> } options.feeds
 * @param {string} options.existingNpmrc
 * @returns {string}
 */
const createUserNpmrc = ({ feeds, existingNpmrc }) => {
```

Or referencing a node module...

```javascript
import https from "https";
/**
 *
 * @param {import("http").RequestOptions} options
 * @returns
 */
export const makeRequest = async (options) => {
```

Here are the docs on all the possible things you can do with JSDoc.

[Documentation - JSDoc ReferenceWhat JSDoc does TypeScript-powered JavaScript support?![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/icon/icon-192x192-1.png)JSDoc Reference![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/thumbnail/WtqB9dW6SB7c9og9HQ5fINwM8gNdsEyn--WaLQKBW8RKFRKniJQqNU8BKFRqngJQqNLwEZWErzngOVuwAAAABJRU5ErkJggg---1)](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html?ref=jonathancreamer.com)

## Configure Typescript to check Javascript and allow ES Modules

There are a few important flags to have in your `tsconfig.json` and `package.json` to make this all work.

```json
{
  "compilerOptions": {
    "target": "ESNext",
    "lib": ["dom", "ESNext"],
    "module": "ESNext",
    "allowJs": true,
    "checkJs": true,
    "rootDir": "./src",
    "outDir": "./lib",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  },
  "include": ["src"]
}
```

The `checkJs` and `allowJs` will instruct both the Typescript compiler, and VSCode to do typechecking on your JS and thus parse all the JSDoc comments into Typescript.

The `module` being set to `"ESNext"` is what tells TS that you're going to be using ESModules.

If you hate the `import * as` stuff you can set `"allowSyntheticDefaultImports"` and `"esModulesInterop"`.

Running `tsc` against that tsconfig will produce JS files in `./lib` along with the `.d.ts` files created from parsing your JS files, and the JSDoc comments.

Lastly, in your `package.json` if you want to, you can set `"module": true` that will tell `node` when you run it that you're using ES Modules, and as of a few node versions ago, the `import` and `export` of modules will just work by running `node path/to/your/script.js`

Read more about node.js's [ES Modules](https://nodejs.org/api/esm.html?ref=jonathancreamer.com) and about how [Typescript handles ES Modules](https://www.typescriptlang.org/docs/handbook/2/modules.html?ref=jonathancreamer.com) as well.

## This works so well in VSCode

The best thing ever is seeing the magic of all this working in VSCode. 😍

You get full intellisense and code completion like you would using native .ts files.

![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/11/image-1.png)

showing the param flyover

![](https://storage.ghost.io/c/42/63/4263df8f-a33f-403d-aa31-ba113a54d1b7/content/images/2024/11/image-3.png)

Showing the full debounce function (thanks to my co-worker Stu for the nice debounce😉)

You get the goodness of Typescript without *needing* to do a single `yarn build` or `npm run build` or anything, because, guess what, you're using native Javascript!

## Conclusion

Lots of open source tools have moved towards this model, including, but not limited to [webpack](https://github.com/webpack/webpack?ref=jonathancreamer.com), and [svelte](https://github.com/sveltejs/svelte/blob/main/packages/svelte/src/internal/server/index.js?ref=jonathancreamer.com).

There are definitely still advantages to using full on `.ts` extensions, and using the `tsc` compiler, or `swc` / `babel` etc to do the transpiling work, but it's nice to know that you don't have to go for a full typescript build for every part of your code base.

This type of programming works great for scripting where you just want to automate some tasks, do system level work, write a CLI, etc. The feedback loop is very fast because there's no compile step at all.

Go forth and write more native Javascript with type checking!