1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
1
import { IContext } from "../Interfaces" ;
3
2
import { ASTNode , ArrayLiteralNode , ArrayNodeValue , Context , Token , TokenType , ValidFuncNames } from "./FormulaEvaluation.types" ;
4
3
@@ -31,7 +30,7 @@ export class FormulaEvaluation {
31
30
}
32
31
33
32
/** Evaluates a formula expression and returns the result, with optional context object for variables */
34
- public evaluate ( expression : string , context : Context = { } ) : any {
33
+ public evaluate ( expression : string , context : Context = { } ) : boolean | string | number | ArrayNodeValue | object {
35
34
context . me = this . _meEmail ;
36
35
context . today = new Date ( ) ;
37
36
const tokens : Token [ ] = this . tokenize ( expression , context ) ;
@@ -240,7 +239,8 @@ export class FormulaEvaluation {
240
239
return stack [ 0 ] as ASTNode ;
241
240
}
242
241
243
- public evaluateASTNode ( node : ASTNode | ArrayLiteralNode | ArrayNodeValue | string | number , context : Context = { } ) : any {
242
+ public evaluateASTNode ( node : ASTNode | ArrayLiteralNode | ArrayNodeValue | string | number , context : Context = { } ) :
243
+ boolean | number | string | ArrayNodeValue | object {
244
244
245
245
if ( ! node ) return 0 ;
246
246
@@ -293,34 +293,40 @@ export class FormulaEvaluation {
293
293
// OPERATOR nodes have their OPERANDS evaluated recursively, with the operator applied to the results
294
294
if ( node . type === "OPERATOR" && operatorTypes . includes ( node . value as string ) && node . operands ) {
295
295
296
- const leftValue = this . evaluateASTNode ( node . operands [ 0 ] , context ) ;
297
- const rightValue = this . evaluateASTNode ( node . operands [ 1 ] , context ) ;
296
+ const leftValue = this . evaluateASTNode ( node . operands [ 0 ] , context ) as string | number ;
297
+ const rightValue = this . evaluateASTNode ( node . operands [ 1 ] , context ) as string | number ;
298
+
299
+ // These operators are valid for both string and number operands
300
+ switch ( node . value ) {
301
+ case "==" : return leftValue === rightValue ? 1 : 0 ;
302
+ case "!=" : return leftValue !== rightValue ? 1 : 0 ;
303
+ case "<>" : return leftValue !== rightValue ? 1 : 0 ;
304
+ case ">" : return leftValue > rightValue ? 1 : 0 ;
305
+ case "<" : return leftValue < rightValue ? 1 : 0 ;
306
+ case ">=" : return leftValue >= rightValue ? 1 : 0 ;
307
+ case "<=" : return leftValue <= rightValue ? 1 : 0 ;
308
+ case "&&" : return ( leftValue !== 0 && rightValue !== 0 ) ? 1 : 0 ;
309
+ case "||" : return ( leftValue !== 0 || rightValue !== 0 ) ? 1 : 0 ;
310
+ }
298
311
299
312
if ( typeof leftValue === "string" || typeof rightValue === "string" ) {
300
- // Throw an error if the operator is not valid for strings
301
- if ( [ "-" , "*" , "/" ] . includes ( node . value as string ) ) {
302
- throw new Error ( `Invalid operation ${ node . value } with string operand.` ) ;
303
- }
304
313
// Concatenate strings if either operand is a string
305
314
if ( node . value === "+" ) {
306
- return ( leftValue || "" ) . toString ( ) + ( rightValue || "" ) . toString ( ) ;
315
+ const concatString : string = ( leftValue || "" ) . toString ( ) + ( rightValue || "" ) . toString ( ) ;
316
+ return concatString ;
317
+ } else {
318
+ // Throw an error if the operator is not valid for strings
319
+ throw new Error ( `Invalid operation ${ node . value } with string operand.` ) ;
307
320
}
308
321
}
309
322
323
+ // Both operands will be numbers at this point
310
324
switch ( node . value ) {
311
325
case "+" : return leftValue + rightValue ;
312
326
case "-" : return leftValue - rightValue ;
313
327
case "*" : return leftValue * rightValue ;
314
328
case "/" : return leftValue / rightValue ;
315
- case "==" : return leftValue === rightValue ? 1 : 0 ;
316
- case "!=" : return leftValue !== rightValue ? 1 : 0 ;
317
- case "<>" : return leftValue !== rightValue ? 1 : 0 ;
318
- case ">" : return leftValue > rightValue ? 1 : 0 ;
319
- case "<" : return leftValue < rightValue ? 1 : 0 ;
320
- case ">=" : return leftValue >= rightValue ? 1 : 0 ;
321
- case "<=" : return leftValue <= rightValue ? 1 : 0 ;
322
- case "&&" : return ( leftValue !== 0 && rightValue !== 0 ) ? 1 : 0 ;
323
- case "||" : return ( leftValue !== 0 || rightValue !== 0 ) ? 1 : 0 ;
329
+
324
330
case "%" : return leftValue % rightValue ;
325
331
case "&" : return leftValue & rightValue ;
326
332
case "|" : return leftValue | rightValue ;
@@ -330,7 +336,10 @@ export class FormulaEvaluation {
330
336
// Evaluation of function nodes is handled here:
331
337
332
338
if ( node . type === "FUNCTION" && node . operands ) {
333
- const funcArgs = node . operands . map ( arg => this . evaluateASTNode ( arg , context ) ) ;
339
+
340
+ // Evaluate operands recursively - casting to any here to allow for any type of operand
341
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
342
+ const funcArgs = node . operands . map ( arg => this . evaluateASTNode ( arg , context ) ) as any [ ] ;
334
343
335
344
switch ( node . value ) {
336
345
0 commit comments