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).
In this second post in our two-part series on TypeScript, we will discuss the various places that you can use TypeScript within the Salesforce ecosystem.
We strongly recommend that you read the previous post (Part 1), before reading this one. In it, we explain what TypeScript is and how is it different from JavaScript. We cover how you can gradually adopt TypeScript into your projects by choosing the level of type verification strictness that you need. We also discuss how VS Code has an inbuilt TypeScript language service that shows suggestions and highlights errors in your code as you finish typing. Finally, we provide a glimpse into various TypeScript concepts, such as types and interfaces, generics, enums, and modules.
Now that you have a basic understanding of how TypeScript works, let’s see where and how you can use TypeScript with Salesforce.
Lightning Web Components
Currently, Lightning Web Components (LWC) has limited support for TypeScript. It is not possible to do type checking between the JavaScript file and HTML template or to do type checking between components. For example, there is no way to ensure that the property types passed from a parent component will match the expected type in the child component. Also, TypeScript files (*.ts
) present in an LWC bundle that’s deployed to the Salesforce Platform can not be processed by the LWC platform compiler.
However, you can take full advantage of VS Code’s TypeScript language service and use the lesser strict levels of TypeScript as described in the previous post. You can even leverage TypeScript declaration files to declare types that can be used across different components. Since the language service just provides static analysis, you needn’t worry about compiling or adding additional steps to your deployment.
To get started, you first need to specify the types using JSDoc constructs. Then, add the flag //@ts-check
at the beginning of the JavaScript file to enable type checking. VS Code will now highlight type errors as shown in the screenshot below.
As a best practice, enable type checking for all of your LWC components at once by adding the checkJs
flag to the compilerOptions
in the jsconfig.json
file present in your SFDX project’s lwc
folder. Also, make sure you add ESNext
as a target
in the compiler options (the default if unspecified is ES3). This will prevent errors from showing up on ES5+ features like get/set accessors.
With the checkJs
flag added, you don’t need to add //@ts-check
to each file anymore. Instead, to skip type checking on specific files, add the //@ts-nocheck
flag at the beginning of those files.
When working with Salesforce data, you can define types for each object you work with. In case you are wondering about the object definitions in the .sfdx/typings
/lwc/sobjects/
folder, those are type declarations for individual fields you import via @salesforce/schema/<objecname>.<fieldname>
. These are automatically created by the Salesforce Extension Pack. While it defines the type of individual fields, it doesn’t hold the definition of the object itself.
So here is how you define an object type, for example, a Contact.
VS Code will pick this up, enforce types, and show suggestions automatically. In the below GIF, you can see that when you type a dot(.
) after contacts
, it shows array methods like at
, fill
, every
, etc. since contacts
is an array. But when you type a dot(.
) after contacts[0]
, it shows the properties of the Contact type that you defined earlier.
Types defined in one component can be imported inside another. Here is how a component HelloBinding
would import the type Contact
defined in the Hello
component.
To ensure that your custom type definitions are available across all your components without having to manually import them, you can create a TypeScript declaration file (*.d.ts
) in the root of your project, and declare types using either type aliases or interfaces. For example, you can create a file mytypes.d.ts
and create two types.
Next, you need to instruct TypeScript to also look at this file to find the type declarations. You can do this by adding the file’s location to the include
property of the jsconfig.json
file present in your SFDX project’s lwc
folder.
You can now use these types in your LWC components using the JSDoc syntax, without having to import them.
The declaration file can be committed to your source control so that every developer on the project can use the same file.
The .sfdx/typings
folder in your SFDX project also contains pre-defined type declarations for built-in modules like Lightning Message Service and Lightning Data Service adapters. Since this folder’s location is also present in the include
property of the jsconfig.json
, you can use these pre-defined types in your components. For example, the result of a getRecord operation has a certain shape, i.e., has pre-defined properties that don’t change based on the object you are querying. You can use VS Code’s suggestions to infer and import such types.
If you want stricter type checking, or want to author your components using .ts
files, you need to first run the TypeScript compiler before deploying your components to the Salesforce Platform. One option is to create a predeploy
Salesforce CLI hook that runs the tsc
command to compile your TypeScript files to JavaScript files.
Lightning Web Runtime
When using Lightning Web Runtime (LWR) to create single-page applications that run off-platform, you can choose to use the TypeScript variant of LWC during project creation.
This creates a project that uses TypeScript files (*.ts
) for Lightning Web Components instead of JavaScript. LWR automatically compiles these files before sending them to the client (browser). If not already present, be sure that you create a tsconfig.json
file at the root of the project.
Below is an example of a simple TypeScript LWC component that uses standard types and custom interfaces. While Line 17 works correctly at runtime, TypeScript treats it as an error, since we are trying to set a property that isn’t valid for that type.
Even here, you can create your own type declaration files to support your implementation. As a best practice, create a types
folder in the root of your project, and place your *.d.ts
files in this folder.
Next, add the path of the types
folder to the include
property of tsconfig.json
.
You can now use these custom types in your components.
Salesforce Functions
TypeScript is one of the supported languages for creating Salesforce Functions — just pass typescript
to the -l
flag when running the generate function
command.
This command creates a TypeScript function that is structured exactly like a typical TypeScript project with files like index.ts
and tsconfig.json
.
A TypeScript function additionally includes the Salesforce Function SDK for Node.js (sf-fx-sdk-nodejs
) as a dependency in the package.json
file. This SDK provides a bunch of different pre-defined types and interfaces that you can import and use in your code. Thanks to these pre-defined types, you know precisely the shape of a parameter, property, or return type, which makes working with Salesforce data a lot easier.
You can even create your own types or import types from a third-party library if you are using one. For example, we can install the node-fetch
library (see docs) from npm and use the types defined by the library. In the below example, we are importing and using the ReferrerPolicy
type from the library.
Also, check out this TypeScript function recipe that returns the Salesforce org information attached to the context.
The @salesforce/ts-types
library
The @salesforce/ts-types
library (see docs) provides a collection of commonly desired types and a collection of type-narrowing functions for writing concise type-guards. For example, the AnyJson
type can be used to hold any untyped JSON object, and different functions like hasBoolean
, isBoolean
, getBoolean
can get the value of a specific type from the JSON object.
The use of this library also ensures runtime type safety. Here is an example:
Salesforce CLI plug-ins
Built on top of OCLIF, a Salesforce CLI plug-in is created using TypeScript. This plugin also uses the @salesforce/ts-types
library mentioned earlier. You can also see the concept of generics in action when using the query
function from the JSforce library. In the below example, you can see that a custom interface Organization
has been created, and is explicitly set as the type to the generic query
function, which automatically sets the type of result
to Organization
.
Slack apps
One of the many ways to build Slack apps is to use Node.js with the @slack/bolt
library added as a dependency. You can easily add typescript
as an additional dependency and start using TypeScript to build Slack apps. Here is a sample Bolt app that is built using TypeScript. Similar to the Salesforce Function SDK for Node.js, the Bolt library also comes with a bunch of pre-defined types like SlashCommand
, SlackEvent
, MessageEvent
, BlockAction
, and many many more. The documentation for using TypeScript with Slack is a work in progress, so keep an eye out for updates.
Summary
The opportunities to use TypeScript with varying levels of strictness across different Salesforce products is gradually increasing, and hopefully, this blog post has given you a good glimpse into them. Now that you have seen how TypeScript can help you write robust code that will result in fewer runtime errors, how about giving it a try?
Here are some resources that will help you along the way:
- Types and tools for Salesforce TypeScript development
- Functions recipes
- Mulesoft’s DataWeave type system
- Getting started with TypeScript on Heroku
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.