Improve productivity with TypeScript and lifecycle hooks for Cloud Functions

If you’ve seen any of my recent Firebase talks, you know I’m a huge fan of TypeScript. At this year’s Firebase Dev Summit in Amsterdam, I recommended TypeScript to improve the quality of your Cloud Functions. Today, we’re making it easier to use TypeScript when writing Cloud Functions.

TypeScript support for Cloud Functions

TypeScript is an extension of JavaScript to help build apps more quickly and correctly. It helps us build apps quickly by giving us early access to the newest JavaScript features like await and async. TypeScript also adds optional static typing to your code. Static typing lets IDEs give better code complete, linters catch more complex bugs, and compilers catch all syntax errors. Many developers have expressed interest in using TypeScript with Cloud Functions. Now starting with 3.16.0, the Firebase CLI gives first-class support to TypeScript. Get the latest version of the CLI with the command:

npm install -g firebase-tools

The new version of the Firebase CLI will ask you to pick a language when you create a new Firebase project with firebase init and choose to use Cloud Functions. If you choose TypeScript, it will set up a TypeScript-ready project structure and compiler options for Cloud Functions.

Because Cloud Functions runs JavaScript, you need to “transpile” your TypeScript into JavaScript before it can run on Node.js. The Firebase CLI understands this, and all TypeScript projects you initialize with the new CLI are automatically compiled as part of every code deployment.

Linting TypeScript Code

When you initialize your TypeScript project, the Firebase CLI recommends you use TSLint. We combed through every rule in TSLint to pick a set of safe defaults. We try not to enforce our coding style, but we will prevent deploys if we’re fairly certain your code has a bug. This includes the most common error when writing Cloud Functions: forgetting to return a promise!

TSLint can detect warnings and errors. Warnings are shown during deploys and errors will block deploys to protect you from breaking production. If you’re absolutely sure that your code is bug-free, you can disable the linter on specific lines with rule flags:

/* tslint:disable:<rule> */
myCode();
/* tslint:enable:<rule> */ 

Or you can disable the rule globally by removing the rule from tslint.json.

Lifecycle Hooks in the Firebase CLI

The Firebase CLI is able to automatically transpile and lint your TypeScript code thanks to another new Firebase CLI feature: lifecycle hooks. These hooks let you add code that should run automatically before. The first two hooks, “predeploy” and “postdeploy”, run before and after a feature is deployed. These hooks work with all Firebase features (Cloud Functions, Hosting, Storage, Database, etc). With these hooks you can:

  • Compile markdown, API docs, or Babel code in Firebase Hosting before deploys
  • Prevent deploys if git has outstanding changes
  • Maintain a git tag for the current version running in production
  • Announce changes to production in your team’s Slack channel

To add a lifecycle hook, add either “predeploy” or “postdeploy” as a subkey in that feature’s stanza of firebase.json. For example, this is the predeploy hook that compiles typescript before deploying:

{
  "functions": {
    "predeploy": "npm --prefix functions run build"
   }
}

The following postdeploy hook tags the current git commit as production (warning: this assumes you don’t have a branch named ‘production-functions’).

{
  "functions": {
    "postdeploy":""git tag -f production-functions && git push -f origin production-functions"
  }
}

Getting Started

We’ve extended our Cloud Functions docs with information about TypeScript.

Let us know how TypeScript affects your development process by tweeting @Firebase. Has it helped catch bugs early? What linter rules did we miss? What are your favorite lifecycle hooks?