Edit (Oct. 2024) – Salesforce type definitions are now available in developer preview. For more information, and up-to-date setup instructions, see TypeScript Type Definitions for LWC (Developer Preview).
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.
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.jso
n
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.
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.
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.
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.
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.
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.