TypeScript (TS) has been increasing in popularity over the last couple of years. Did you know that you can use TypeScript with Lightning Web Components (LWC), and that there are several other Salesforce products and features that support TypeScript, such as Salesforce Functions, Lightning Web Runtime, and more?

In this first post of a two-part series, we’ll cover what TypeScript is, how you can install and use it in your projects, and the different data types that you can create with TypeScript. In the second post, we’ll cover the various places where you can use TypeScript within the Salesforce ecosystem.

What is TypeScript?

TypeScript is strongly-typed JavaScript (JS). It offers all of JavaScript’s features and an additional layer on top: TypeScript’s type system. TypeScript can’t run on its own; instead, TypeScript code complies to standards-based JavaScript.

How is it different from JavaScript?

JavaScript is a dynamically typed language, which means that it needs to be run to confirm that your business logic works as expected. For example, due to Type coercion, the output might be different from what you expect. TypeScript, on the other hand, offers features like static typing and a compiler that help you detect errors during compile time itself, which would otherwise pop up during runtime. This could include type errors, typos, usage of incorrect methods, uncalled functions, basic logic errors, non-exception failures, null and undefined checks, and so on. TypeScript also offers additional features, such as generics, interfaces, and enums, that encourage proper design and guide developers during development and code refactoring. We will cover some of these features later in this blog post.

An image showing a list of features that TypeScript adds on top of JavaScript, and a compiler that converts TS files to JS files

Installing and compiling TypeScript

The TypeScript compiler is available as an npm package that can be installed in any of your projects. Once installed, you can start creating TypeScript files (*.ts). The compiler compiles the .ts files into standard .js files. It can be configured to target a specific version of ECMAScript (ES5, ES6, etc.). You will need to add a new script to the scripts section of the package.json file to use the compiler.

The tsconfig.json or jsconfig.json file can be used to control the behavior of TypeScript and the compiler in your project. Check out the documentation for various config options you can use.

During the compilation process, most of the type information is stripped away, with a few exceptions that we’ll discuss later in this blog post. Below is an example that shows the compiled JavaScript code for a given TypeScript code.

A screenshot that shows a TypeScript code snippet on the left, and the resultant JavaScript code after compiling on the right

However, it is important to note that the TypeScript compiler does not modify the runtime behavior or logic of your JavaScript code.

Take this TypeScript code for example. It contains a printValue function that logs a value that is sent to it. The type of the val parameter is set to string. This code snippet throws a compile-time error at Line 6 because we are trying to assign a boolean value to a string.

 A screenshot that shows an error in VS Code that shows up in a TypeScript code snippet when a boolean value is assigned to the val parameter

However, once compiled, here is what the resulting JavaScript file looks like. There is no additional code generated that enforces the assignment of the val parameter to only string values. Hence, Line 6 would execute without any errors during run time.

A screenshot that shows no error in a JavaScript code snippet when a boolean value is assigned to the val parameter

You might be wondering: how did the JavaScript file get generated if there was a compile-time error? This is the default behavior of the compiler, which generates the JavaScript files even though there are type errors. You can set the --noEmitOnError flag (see docs) when running the compiler to prevent this from happening.

If you are using an editor with TypeScript plugins like VS Code, you needn’t always compile your files to find type errors. VS Code comes with built-in TypeScript support via the TypeScript language service, which shows suggestions and highlights errors as you finish typing. This is majorly helpful when trying to gradually adopt TypeScript into your projects.

Varying levels of strictness

Existing JavaScript projects can be incrementally migrated to TypeScript, i.e., you needn’t convert all your *.js files to *.ts at once. But you can still take advantage of TypeScript’s type system in your existing JavaScript projects. Below are the five stages in which you can adopt TypeScript, and each stage makes the type verification more strict.

A screenshot that shows the five stages of type verification

In the first three stages, you will be working on JavaScript files themselves, but you can still use the TypeScript compiler or VS Code’s TypeScript language service to find errors. However, type checking works a bit differently in JS files. Check out this guide to know more.

To enable type checking on a few JavaScript files, add //@ts-check at the beginning of the files that you want to check. If you want to check all the files in the project, you can add the properties checkJs and allowJs to either the tsconfig.json file or pass them as flags to the compiler.

In the first two stages, TypeScript tries to infer data types based on your usage and point errors. If you are using VS Code, you can see the errors in the Problems tab even without running the compiler.

A screenshot that shows a JavaScript code snippet with the ts-check flag, and an error that is shown in VS Code when a string is assigned to a variable that is originally initialized to a number

In the next stage, you can enforce types explicitly by specifying the data types of your variables via JSDoc constructs.

Once you are comfortable, you can start creating TypeScript files and take complete advantage of the type system.

Working with TypeScript

While the official TypeScript documentation provides a fairly detailed explanation, here is a quick summary of a few important concepts.

Types and interfaces

You can assign any of the standard data types to variables, input and output values for functions, etc. You can also use the special type any whenever you don’t want a particular value to cause type-checking errors.

You can even combine two or more types and create a union type. Here is a simple example:

You can create a type alias to reuse a type definition in multiple places.

You can also define types for non-primitives like objects. There are multiple ways to define object types (i.e, the shape of an object). First, you can define them inline.

Second, you can create a type alias.

Finally, you can create an interface. The only difference between a type alias and an interface is that you can add new properties to an interface but not to a type alias.

Generics

It is common that you create functions or classes that can work with different data types, but using the any type for those will not guarantee type safety. That’s where generics come into the picture. Generics allows you to pass types as arguments when calling a function or initializing a class, along with its values.

In the below example, we create a function query that takes in a SOQL query as a parameter and returns the results. The type of the results is obviously based on the object we are querying. So, here is how generics can be used to make sure the query function is type-safe.

You can see generics in action when you are building with Salesforce CLI plug-ins; we will cover this in Part 2 of this series.

Enums

Enums is one of the features offered by TypeScript that isn’t just for type safety. Similar to Apex, enums allow a developer to define a set of named constants. You can either initialize the constants to a particular value, or they default to auto-incremented numbers. Unlike the other type definitions, enums aren’t removed from the final JavaScript code during compilation, and they act like objects at runtime. const enums on the other hand, are completely removed during compilation.

Modules

The concept of modules is the same in both JavaScript and TypeScript. In TypeScript, you can additionally export and import type declarations like aliases and interfaces.

TypeScript also allows you to declare Ambient Modules to specify the shapes of libraries not written in TypeScript. For example, here is a library (sf-libs) written in JavaScript, that includes a few functions.

When you import this library in a TypeScript file, you would get a type error. This is because, no type information is available for the module and its functions.

Here is a TypeScript ambient module for the sf-libs JavaScript module. It is important to remember that ambient modules just define the shape of the module, and don’t contain any implementation details.

Checkout the ambient module declaration for the Lightning Web Components core module.

Summary

This post was just a glimpse into how you can use and gradually adopt TypeScript into your projects. You can dive deep into TypeScript using the below resources:

In the second post in this series, we will go over the various places where you can use TypeScript within the Salesforce ecosystem.

About the author

Aditya Naag Topalli is a 14x Certified Lead Developer Advocate at Salesforce. He empowers and inspires developers in and outside the Salesforce ecosystem through his videos, webinars, blog posts, and open source contributions, and he also frequently speaks at conferences and events all around the world. Follow him on Twitter or LinkedIn and check out his contributions on GitHub.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS