Published on

Using Typescript Without Typescript

5 min read
Authors

I'm a big fan of Typescript. I like to use it in all my projects where possible. Typescript provides error reporting and type checking of your Javascript/Typescript code. It also provides you with intellisense and safe refactoring in the form of quick fixes. It's your first line of defence against erronious code. Another benefit of Typescript is that it allows you to use the latest Javascript language features when writing your code, but the output is down-compiled to javascript that all browsers support. Neat.

However, not all frontend projects are set up to use Typescript. Wouldn't it be great to get the benefits of Typescript without forcing this on your entire project (and team) and without adding a new tool into your frontend build process? With VS Code and JSDoc - you can!

Update March 8 - removed excludes from tsconfig.json and jsconfig.json as these are covered by the defaults. Added example of using type declaration files. Added repo for source code.

Setup

Option 1: VS Code - Global

The first option is the easiest and will enable Typescript to check all your Javascript files from a single global setting. The following setting can be enabled in your VS Code user or workspace settings:

"javascript.implicitProjectConfig.checkJs": true

Or if you're a UI guy:

Enabling TS Checking of JS via VS Code

Option 2: jsconfig.json - Global

The next option to enable type checking globally is via a jsconfig.json. If this file is present, it will override any settings you might have enabled in VS Code.

{
  "compilerOptions": {
    "checkJs": true
  }
}

Option 3: per file

The third option is to enable on a per-file basis. This is done with a comment at the top of the file.

// @ts-check
let itsAsEasyAs = 'abc'
itsAsEasyAs = 123 // Error: Type '123' is not assignable to type 'string'

Using the same technique, you may wish to disable Typescript for a single file if it has been enabled globally using options 1 or 2 above. This can be done with the following comment:

// @ts-nocheck
let easy = 'abc'
easy = 123 // no error

Or if you want typescript to ignore just a section of a file you can use:

let easy = 'abc'
// @ts-ignore
easy = 123 // no error

Adding Types with JsDoc

You've now seen how to enable Typescript in your Javascript files. This will provide you with basic type checking. This can be enhanced with defining your types with JsDoc comments.

Adding Types to Functions

You can start by simply defining what your function takes as input:

/**
 * @param {number} shippingVal
 */
updateShipping(shippingVal) {
    ...
}

Your editor will then have Typescript powered intellisense:

TS powered intellisense

This works great for simple types, but what if you want to define your own types? This can be done by using jsdoc>@typedef. I recommend placing these type definitions at the top off your file for ease of discovery:

/**
* @typedef {Object} CreditNoteTaxResponseViewModel
* @property {number} feeAmount
* @property {number} inclGst
* @property {number} subTotal
* @property {number} total
*
* @typedef {Object} ApiResponse
* @property {string} status
* @property {string} message
* @property {CreditNoteTaxResponseViewModel} response
*/

And then used where necessary:

	/**
	 * @param {CreditNoteTaxRequestViewModel} req
	 * @returns {Promise<ApiResponse>}
	 */
	createCreditNoteTaxApiCall(req) {
        ...
	}

Another option is to move these types to their own type declaration file. e.g. main.d.ts

export interface ICreditNoteTaxRequestViewModel {
  orderID: number
  shippingCredit: number
  lines: IICreditNoteTaxLineViewModel[]
}

export interface ICreditNoteTaxLineViewModel {
  originalOrderLineID: number
  creditQuantity: number
}

export interface ICreditNoteTaxResponseViewModel {
  feeAmount: number
  inclGst: number
  subTotal: number
  total: number
}

export interface IApiResponse {
  status: string
  status: message
  response: ICreditNoteTaxResponseViewModel
}

These types can then be referenced in your Javascript as follows:

  /**
   * @param {import("./main").ICreditNoteTaxRequestViewModel} req
   * @returns {Promise<import("./main").IApiResponse>}
   */
  function createCreditNoteTaxApiCall(req) {
    /// snip
    return;
  }

Adding Types to inline code

The above solves the problem of adding types to function inputs and outputs. We can do something similar with inline JsDoc comments:

Inline JsDoc Comments

Adding Types to Libraries

VS Code has automatic type acquisition for 3rd party libraries. This will be applied to any packages in your packages.json file. However, if you prefer this to be explicit, you can configure this in jsconfig.json.

{
  "typeAcquisition": {
    "include": ["jquery"]
  }
}

Once the type acquisition has picked up the library you can then use it with JsDoc:

/**
 * @param {JQuery<HTMLElement>} $itemRow
 */
initRow($itemRow) {
    ...
}

Migrating to Typescript

If you decide to start migrating from a Javascript project with some Typescript, to a full Typescript project, you can simply rename your jsconfig.json to tsconfig.json and add allowJs: true.

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true
  }
}

Once Typescript is enabled in your project, you can start renaming your *.js files to *.ts files one at a time and adding the types. This can be a gradual migration process that happens over time.

Summary

In this article, you've seen how easy it is to get some of the benefits of Typescript in a Javascript project with only the help of VS Code. This approach allows you to keep your current build process exactly as is to avoid disruption and without having to convince your team to go all in.

Later on, if you decide you want to invest more into using Typescript, it is easy to convert to full Typescript and start converting your Javascript files to Typescript files.

Code

The code for this article can be found on Git Hub

Resources