Overview
Build entire SFCC cartridges in TypeScript - full IntelliSense and debugging out of the box, with zero configuration
Intellij SFCC supports TypeScript for SFCC. You can write whole cartridges - controllers, models, hooks, helpers - in .ts files and get the full SFCC code-intelligence experience: typed dw.* APIs, cartridge-aware require(), completion, navigation, diagnostics, and step debugging on a sandbox.
It all works out of the box, with zero configuration. The extension and its TypeScript plugin handle everything - there is no tsconfig.json, type package, or build wiring required just to develop.
Write Cartridges in TypeScript
A TypeScript cartridge file is treated as sfcc-typescript and runs on the same SFCC engine as your JavaScript. You write real TypeScript - interfaces, enums, generics, access modifiers, even decorators - against fully typed dw.* APIs, and it compiles to Rhino-safe JavaScript on deploy:
const ProductMgr = require('dw/catalog/ProductMgr');
const URLUtils = require('dw/web/URLUtils');
enum Availability {
InStock = 'in-stock',
OutOfStock = 'out-of-stock',
}
interface ProductTile {
id: string;
name: string;
url: string;
availability: Availability;
}
/** Accessor decorator - memoize a getter's result onto the instance. */
function cached(_target: object, key: string, descriptor: PropertyDescriptor): void {
const getter = descriptor.get!;
descriptor.get = function (this: Record<string, unknown>) {
const store = `__${key}`;
if (!(store in this)) {
this[store] = getter.call(this);
}
return this[store];
};
}
class ProductTileBuilder {
private readonly product: dw.catalog.Product;
constructor(product: dw.catalog.Product) {
this.product = product;
}
@cached
get url(): string {
return URLUtils.url('Product-Show', 'pid', this.product.ID).toString();
}
build(): ProductTile {
return {
id: this.product.ID,
name: this.product.name,
url: this.url,
availability: this.product.availabilityModel.inStock
? Availability.InStock
: Availability.OutOfStock,
};
}
}
function getProductTile(id: string): ProductTile {
return new ProductTileBuilder(ProductMgr.getProduct(id)).build();
}
module.exports = { getProductTile };Every TypeScript feature above - the enum, interface, the @cached decorator, the class with a private readonly field - is type-checked against the full B2C Commerce API (dw.catalog.Product, its availabilityModel, ID, and name are all typed) and down-leveled to Rhino-safe JavaScript by the plugin and your build. See Types & the dw API, Module Resolution, and Build & Transpile for how each is compiled.
Write the same CommonJS you'd write in .js: const x = require(...) and module.exports - not ESM import x from / export default. SFCC runs on the Rhino engine, which has no ES-module loader, and plain require() is also what gets your dw.* modules typed correctly. See Imports & the Rhino Engine.
SFCC scripting is synchronous - there is no Promise, async/await, setTimeout, or fetch. Rhino doesn't provide them and the transpile does not polyfill them, so async code won't run on the server no matter how it compiles. Down-leveling rewrites modern syntax; it does not add missing runtime built-ins. Use the synchronous dw.* APIs (e.g. dw.net.HTTPClient for I/O). The full list is in Imports & the Rhino Engine.
Debug TypeScript on a Sandbox
You can set breakpoints directly in your .ts source and debug live against a sandbox. The extension contributes an SFCC debugger that connects to the Salesforce B2C Commerce Script Debugger (SDAPI) over HTTPS, and it maps the running JavaScript back to your TypeScript source so breakpoints, stepping, the call stack, and variable inspection all line up with the code you actually wrote.
Pick the SFCC debug configuration
Open Run and Debug, choose Salesforce B2C Commerce (SFCC), and start it. There is no connection detail to hand-write - the debugger uses your active connection from the Intellij SFCC sidebar.
Set breakpoints in TypeScript
Put breakpoints in your .ts files. The extension translates them to the deployed JavaScript on the sandbox.
Trigger the code
Hit the storefront, controller, hook, or job that runs your code. Execution halts at your breakpoint, shown in your TypeScript source with the correct line and variables.
Debugging maps to the JavaScript that is deployed in the active code version. Keep the compiled output that is on the sandbox in sync with your local TypeScript so breakpoint lines align - the mapping uses the extension's transpilation metadata, not .js.map files from the server.
See Debugging for the full debugger workflow.
The Boundary: Editing vs. Deployment
This is the one thing to internalize about TypeScript on SFCC:
- Type-aware editing of
.tswith zero config. - Compiling
.ts→ Rhino-safe.jswith Babel and uploading it to your active sandbox, so your TypeScript runs there as you develop. - Source maps for debugging - breakpoints in
.tsmap to the running JavaScript.
- Building and deploying a release code version to shared/PIG/production instances.
- Transpiling to the same Rhino-safe output the plugin produces - the simplest way is to reuse its Babel config, but any transpiler that meets the rules works.
- Packaging that ships only the emitted
.js- run from your build pipeline, not the editor.
The extension already gets your TypeScript running on the sandbox you're connected to - it compiles with Babel and uploads the .js for you. What it does not do is drive your release pipeline to other instances; that build-and-deploy step is yours. It should produce the same Rhino-safe JavaScript the plugin does - reuse the plugin's Babel config to match it exactly, or use any toolchain that satisfies the output rules.
We give you a ready-to-use, Rhino-safe setup to drop into your pipeline. See Build & Transpile (CI/CD).