How To Migrate Firebase Cloud Functions – JavaScript To TypeScript

Firebase Cloud Functions JavaScript Migrating to TypeScript

Firebase cloud functions support several languages and execution environments including Javascript and Typescript. Recently I did a JavaScript to Typescript migration and followed the recommended migration steps.

~ 3 minutes read

Why Typescript?

By understanding JavaScript, TypeScript saves you time catching errors and providing fixes before you run code.

https://www.typescriptlang.org/

Firebase cloud functions written in Javascript do not allow to define type annotations such as return types.

Firebase Cloud Functions JavaScript Migrating to TypeScript

This was a bummer for me. Typescript code for Firebase cloud functions allow to define return types, so I started the migration to TypeScript.

CLI command prompts

firebase init functions is the main CLI command to convert such a Javascript codebase to Typescript. Apart from the documented prompts I experienced these additional questions:

Not to start from scratch with the (es)lint setup is just great.

Firebase imports & Typescript options

The existing javascript imports did not follow typescript conventions. I did change several import statements similar to this:

Also I enriched the firebase generated file tsconfig.json based on https://stackoverflow.com/a/63434005 :

The option esModuleInterop was really helpful with import issues like these:

Node.js Runtime

Cloud functions support at the time of writing 3 node.js runtimes:

  • Node.js 10
  • Node.js 12
  • Node.js 14

https://firebase.google.com/docs/functions/manage-functions#set_nodejs_version

NVM is a handy command line tool to switch between different node runtimes. It’s a CLI tool that can help to match one of the supported runtimes. Just call nvm use [node] to switch the node version of your choice.

Linting

I spend the most time fixing eslint errors to be able to deploy. This way I realized how relaxed javascript code can be.

The javascript code in functions/lib directory was not touched in the migration attempt so far. However, I got several linter findings about that code. I deleted all javascript files in that directory. Those files will be generated while deploying anyway.

Several functions eventually returned http status codes. Typescript helped my to make sure a dedicated http status codes is returned for all conditions.

Cloud Firestore triggers enable functions code to deal on firestore document changes. Functions’ parameter for those functions include change and context. If context is not used in the function body, eslint warns about no-unused-vars. I deleted the context parameter for all functions.

I decided not to fix some eslinter errors, rather I ignore those. I came accross javascript code lines that caused multiple eslinter errors. https://stackoverflow.com/a/56714489 list options how to disable multiple linter errors:

Last but not least I had to deal with quite some eslinting issues that may come up not only in a javascript to typescript migration but also when you change the linter rules.

Function parameter types

The javascript code base I worked with did not define function parameter types. VS Code’s feature Infer all types from usage fixed this issue for most of the cases. Type inference is a real time saver.

Time safe string compare

Http headers’ verification was done with tsscmp based on github.com/suryagh/tsscmp. The respective typescript code looks significantly different and is inspired by github.com/IBM/diem/…/verifysignature.ts.

Code snippets

These are code snippets from the code sections above as text.

? Do you want to use ESLint to catch probable bugs and enforce style? (Y/n)
? File functions/.eslintrc.js already exists. Overwrite? (y/N)
? Do you want to install dependencies with npm now? (Y/n)
import * as functions from "firebase-functions";
import admin from "firebase-admin";
{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017",
    // https://stackoverflow.com/a/63434005
    // json imports
    "resolveJsonModule": true,
    // import common modules as ES6 Modules
    "esModuleInterop": true,
    // support typesystem compatibility
    "allowSyntheticDefaultImports": true,
  },
  "compileOnSave": true,
  "include": [
    "src"
  ]
}
exports.documentChanged = functions.firestore
  .document('some-collection/{docId}')
  .onWrite((change, context) => { /* ... */ });

// warning 'context' is defined but never used @typescript-eslint/no-unused-vars
const format = /(?:[a-z0-9!.....

// linter error
196:1   error  This line has a length of 446. Maximum allowed is 80 max-len
196:18  error  Unexpected control character(s) in regular expression: ....

// workaround:
/* eslint-disable-next-line no-control-regex, max-len*/
Strings must use doublequote
There should be no space after '{'
There should be no space before '}'
Expected indentation of X spaces but found Y
Missing trailing comma

Be the first to comment

Leave a Reply

Your email address will not be published.


*


This site uses Akismet to reduce spam. Learn how your comment data is processed.