JSDoc is the usual way to tell the IDE what a parameter holds. In SFCC code most types are already decided by the server or by the code around them, so Intellij SFCC reads them from there, and completion, Cmd+Click and checks work on code that has no JSDoc at all
collections.map or your own visitors.each, so lineItem is a ProductLineItem, index a number and shipment a Shipment, with no JSDoc on themWhat gets a type
| Code | Typed from | More |
|---|---|---|
A function you pass to a helper, such as collections.map(items, function (lineItem, index) {...}) | What the helper passes it | Callbacks |
req, res and next of an SFRA route | SFRA's typings | SFRA Controllers |
A hook function, such as exports.calculate = function (basket) | The hook point in hooks.json | Hook functions |
| A job step function | The step kind in steptypes.json | Job steps and pipelets |
module.superModule and the scripts that extend it | Every copy below on the cartridge path | superModule chains |
A loop variable, such as for each (var item in basket.productLineItems) | The element of what it iterates | Loop types |
server.forms.getForm('profile'), product.custom.badgeText | The form XML and the metadata XML | Forms, Custom Attributes |
Callbacks
A function you write in the call to a helper gets the types of the values the helper passes it. The plugin reads the helper, in your cartridges or in SFRA's collections.js, and follows the values into the call
var collections = require('*/cartridge/scripts/util/collections');
/** @param {dw.order.Basket} basket */
function summarize(basket) {
return collections.map(basket.productLineItems, function (lineItem, index) {
// lineItem is dw.order.ProductLineItem, index is number
return { quantity: lineItem.quantityValue, position: index + 1 };
});
}The helper can call the function in any of these ways:
| Helper code | Example |
|---|---|
| An iterator loop | var it = items.iterator(); while (it.hasNext()) visitor(it.next()); |
| An index loop | for (var i = 0; i < items.length; i++) callback(items[i], i); |
call and apply | accept.call(null, cursor.next()), accept.apply(null, [entries[i], i]) |
| An accumulator | callback(initial, iterator.next()), as in collections.reduce |
| A member of the value | done(value.customer) types the parameter as dw.customer.Customer |
| Another helper | relay(input, consumer) that hands both to deliver, up to 3 helpers deep |
Arrow functions work too, at any argument position. The types follow the helper and the call as you edit either one
When a callback stays untyped
The plugin doesn't guess. A parameter keeps the IDE's own type when:
- It has a JSDoc
@paramtype or a TypeScript annotation, which always wins - The helper passes something the plugin can't type, such as the result of an unknown function, or reassigns the value in its loop
- The helper calls itself, is very long, or passes the function through more than 3 helpers
- The function is declared elsewhere and passed by name, such as
collections.map(items, toRow)
Callback types need a license or trial and wait until indexing ends. They work in the .js and .ds files of linked cartridges
Hook functions
In a script that hooks.json registers for a platform hook point, the function named after the hook's last segment gets the hook's parameters, with no JSDoc:
| Hook | Function | Parameters |
|---|---|---|
dw.order.calculate | exports.calculate | (dw.order.LineItemCtnr) |
dw.ocapi.shop.basket.items.beforePOST | exports.beforePOST | (dw.order.Basket, ProductItemDocument[]) |
A function that several hooks share takes the types of all of them, like Basket | Order. These platform types win over a JSDoc @param. The Hook function returns a value its hook does not expect inspection then checks the return value, so return false in calculate reads "dw.order.calculate must return dw.system.Status, not boolean". See Hook function parameters for the hooks covered
Job steps and pipelets
A step function named in steptypes.json gets the parameters of its step kind, such as (item, Object, dw.job.JobStepExecution) for a process-function. A JSDoc @param wins over these. A pipelet's execute(pdict) gets the keys of its @input and @output header. See Parameters the server passes
superModule chains
var base = module.superModule gets the exports of the copy below, through every override on the cartridge path. Two SFRA patterns keep them too:
| Code | base. and require of the script list |
|---|---|
var base = module.superModule || {};, then base.getBadge = getBadge; | The exports of every copy below and the members you add |
module.exports = { getBadge: getBadge };, then module.exports[key] = base[key] in a loop over base | The literal's members and every export below that the literal doesn't name |
module.superModule.getProductType(product), with no variable | The exports of the copy below |
Cmd+Click on an inherited member opens the copy that defines it, and completion names that cartridge on the right. See module.superModule
Troubleshooting
| Symptom | What to do |
|---|---|
| A callback parameter has no type | Check the list in When a callback stays untyped. Write the function in the call, or add a JSDoc @param |
| A callback parameter has the wrong type | A JSDoc @param on it wins, so check the comment above the function |
| A hook function's parameters aren't typed | Register the script in the hooks.json that the cartridge's package.json points to, and export the function under the hook's last segment |
base. lists only your own members | The script must name module.superModule, and the copy below must be in a linked cartridge at the same path, case for case |
| Types appear only after a while | Callback, hook and step types wait until indexing ends |