Filed during PM review of PR #5578 (card #5363). Not a defect — every statement in that block is true, and TypeScript rejects the wrong reading at the call site. Recorded because the imprecision sits in a doc block whose entire subject is a name that does not behave the way it reads.
What is there
packages/core/src/evaluator/ExpressionEvaluator.ts exports two things spelled evaluateExpression:
| entity | line | signature | on failure |
|---|
method on ExpressionEvaluator | :142 | evaluateExpression(expression, { sanitize? }) — takes a bare expression, no ${...} wrapper | throwsFailed to evaluate expression "..." |
| module-level export | :368 | evaluateExpression(expression, context, options) — delegates to evaluator.evaluate(...) | fail-soft; returns defaultValue ?? expression |
PR #5578's new JSDoc on registerFunction refers to both, under the one spelling, four lines apart:
- the prose — "{@link evaluateExpression} is the throwing sibling, and reports
'formatCurrency' is not a function" — resolves, per TSDoc, to the method. Correct. - the final
@example line — evaluateExpression('${formatCurrency(price)}', { formatCurrency: fmt, price: 1234.5 }); // '$1,234.50' — is the module-level export: second parameter is a context bag, and the ${...} wrapper only resolves on the evaluate path. Also correct.
Why it is worth recording rather than fixing in place
Bound to the method instead, that example line does not merely return something else — it throws, because a bare ${formatCurrency(price)} is not a valid JS expression. The two lines above it establish evaluator. as the receiver, and a .d.ts hover carries no import statement to disambiguate.
The harm is bounded, which is why this is a finding and not a rework: the method's second parameter is typed { sanitize?: boolean }, so passing a context bag is a compile error, not a silent wrong answer. The reader is corrected by the compiler at the moment of the mistake.
Shape of a fix
Qualify both references in that block — {@link ExpressionEvaluator.evaluateExpression} for the method, and a receiver or a comment marking the example's last line as the module-level helper. One line, no behaviour, no test.
Refs
Filed during PM review of PR #5578 (card #5363). Not a defect — every statement in that block is true, and TypeScript rejects the wrong reading at the call site. Recorded because the imprecision sits in a doc block whose entire subject is a name that does not behave the way it reads.
What is there
packages/core/src/evaluator/ExpressionEvaluator.tsexports two things spelledevaluateExpression:ExpressionEvaluatorevaluateExpression(expression, { sanitize? })— takes a bare expression, no${...}wrapperFailed to evaluate expression "..."evaluateExpression(expression, context, options)— delegates toevaluator.evaluate(...)defaultValue ?? expressionPR #5578's new JSDoc on
registerFunctionrefers to both, under the one spelling, four lines apart:'formatCurrency' is not a function" — resolves, per TSDoc, to the method. Correct.@exampleline —evaluateExpression('${formatCurrency(price)}', { formatCurrency: fmt, price: 1234.5 }); // '$1,234.50'— is the module-level export: second parameter is a context bag, and the${...}wrapper only resolves on theevaluatepath. Also correct.Why it is worth recording rather than fixing in place
Bound to the method instead, that example line does not merely return something else — it throws, because a bare
${formatCurrency(price)}is not a valid JS expression. The two lines above it establishevaluator.as the receiver, and a.d.tshover carries no import statement to disambiguate.The harm is bounded, which is why this is a finding and not a rework: the method's second parameter is typed
{ sanitize?: boolean }, so passing a context bag is a compile error, not a silent wrong answer. The reader is corrected by the compiler at the moment of the mistake.Shape of a fix
Qualify both references in that block —
{@link ExpressionEvaluator.evaluateExpression}for the method, and a receiver or a comment marking the example's last line as the module-level helper. One line, no behaviour, no test.Refs
ExpressionEvaluator.registerFunctionupper-cases the name, so a lower-case call in an expression renders its own${...}source #5363 / PR docs(core): state the registerFunction case-fold on the method itself #5578 — where the block was addedpackages/core/src/evaluator/ExpressionEvaluator.ts:142(method) and:368(export)const mergedContext = { ...formulaObj, ...contextObj }at :160 — context is spread last, so context entries win over formulas and keep their exact spelling