All files / src/ast/operator op-instanceof.ts

100% Statements 35/35
100% Branches 12/12
100% Functions 3/3
100% Lines 35/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48  1x 1x   1x   1x 1x 13x 13x 13x 13x 13x 13x 13x   1x 13x 13x     13x 2x 2x 2x     11x   13x 2x 2x   9x 13x   1x 13x 13x 2x 2x 2x 2x 2x   13x 1x  
import type { ExpressionState } from '../../expression-state.js';
import { TypedValue } from '../../typed-value.js';
import { Operator } from '../spel-node.js';
import type { TypeDescriptor } from '../../type/type-descriptor.js';
import { NodeType } from '../../language/node-type.js';
 
export class OpInstanceof extends Operator {
  constructor(
    operatorName: string,
    startPos: number,
    endPos: number,
    ...operands: import('../spel-node.js').SpelNodeImpl[]
  ) {
    super(NodeType.OP_INSTANCEOF, operatorName, startPos, endPos, ...operands);
  }
 
  public override getValueInternal(state: ExpressionState): TypedValue {
    const left = this.children[0]!.getValue(state).getValue();
    const rightRaw = this.children[1]!.getValue(state).getValue();
 
    // instanceof T(Type): right side resolves to a TypeDescriptor
    if (this.isTypeDescriptor(rightRaw)) {
      const td = rightRaw as TypeDescriptor;
      return new TypedValue(td.isInstance(left));
    }
 
    // String-based: 'string', 'number', 'boolean', 'null', 'object'
    const typeName = String(rightRaw);
 
    if (typeName === 'null') {
      return new TypedValue(left === null);
    }
 
    return new TypedValue(typeof left === typeName);
  }
 
  private isTypeDescriptor(value: unknown): boolean {
    return (
      typeof value === 'object' &&
      value !== null &&
      'name' in value &&
      'constructor' in value &&
      'isInstance' in value &&
      typeof (value as Record<string, unknown>).isInstance === 'function'
    );
  }
}