Skip to content

Commit

Permalink
fix: update evaluatorCallback types (this, and access to class proper…
Browse files Browse the repository at this point in the history
…ties) (#64)

fix: update evaluatorCallback types (this, and access to class properties)

- fix typings for `evaluatorCallback` to use generics, which allows simpler discriminated unions of specific node types
- fix evaluatorCallback not passing `context` as an arg (was only on `this`)
- make `context` and `isAsync` public so callbacks have access when using `this`

fixes #63
  • Loading branch information
6utt3rfly authored Jul 10, 2022
1 parent cf29567 commit 49a857f
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 14 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,13 @@ expr.addEvaluator('TestNodeType', function(node) {
});
console.log(expr.eval({ type: 'TestNodeType', test: 'testing ' }, { string: 'jse-eval' })); // 'testing jse-eval'

// override default implementation:
expr.addEvaluator('Identifier', function myIdentifier(node: Identifier) {
return context?.[node.name];
});
console.log(expr.eval({ type: 'Identifier', name: 'x' }, { x: 'jse-eval' })); // 'jse-eval'


const myPlugin = {
name: 'Exponentiation',
init(jsep) {
Expand Down
28 changes: 14 additions & 14 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ import jsep from 'jsep';
* Copyright (c) 2013 Stephen Oney, http://jsep.from.so/
*/

declare type Context = Record<string, unknown>;
declare type operand = any;
declare type unaryCallback = (a: operand) => operand;
declare type binaryCallback = (a: operand, b: operand) => operand;
declare type assignCallback = (obj: Record<string, operand>, key: string, val: operand) => operand;
declare type evaluatorCallback = (node: AnyExpression, context: Context) => unknown;
export declare type Context = Record<string, unknown>;
export declare type operand = any;
export declare type unaryCallback = (a: operand) => operand;
export declare type binaryCallback = (a: operand, b: operand) => operand;
export declare type assignCallback = (obj: Record<string, operand>, key: string, val: operand) => operand;
export declare type evaluatorCallback<T extends AnyExpression> = (this: ExpressionEval, node: T, context?: Context) => unknown;

type AnyExpression = jsep.Expression;
export type AnyExpression = jsep.Expression;

type JseEvalPlugin = Partial<jsep.IPlugin> & {
export type JseEvalPlugin = Partial<jsep.IPlugin> & {
initEval?: (this: typeof ExpressionEval, jseEval: typeof ExpressionEval) => void;
}

Expand All @@ -30,7 +30,7 @@ export default class ExpressionEval {
static parse = jsep;
static evaluate = ExpressionEval.eval;

static evaluators: Record<string, evaluatorCallback> = {
static evaluators: Record<string, evaluatorCallback<AnyExpression>> = {
'ArrayExpression': ExpressionEval.prototype.evalArrayExpression,
'LogicalExpression': ExpressionEval.prototype.evalBinaryExpression,
'BinaryExpression': ExpressionEval.prototype.evalBinaryExpression,
Expand Down Expand Up @@ -157,7 +157,7 @@ export default class ExpressionEval {
}

// inject custom node evaluators (and override existing ones)
static addEvaluator(nodeType: string, evaluator: evaluatorCallback): void {
static addEvaluator<T extends AnyExpression>(nodeType: string, evaluator: evaluatorCallback<T>): void {
ExpressionEval.evaluators[nodeType] = evaluator;
}

Expand Down Expand Up @@ -197,8 +197,8 @@ export default class ExpressionEval {
}


protected context?: Context;
protected isAsync?: boolean;
context?: Context;
isAsync?: boolean;

constructor(context?: Context, isAsync?: boolean) {
this.context = context;
Expand All @@ -211,7 +211,7 @@ export default class ExpressionEval {
if (!evaluator) {
throw new Error(`unknown node type: ${JSON.stringify(node, null, 2)}`);
}
return this.evalSyncAsync(evaluator.bind(this)(node), (v) => {
return this.evalSyncAsync(evaluator.bind(this)(node, this.context), (v) => {
(node as any)._value = v;
return cb(v);
});
Expand All @@ -233,7 +233,7 @@ export default class ExpressionEval {
* `promisesOrResults = expressions.map(v => this.eval(v))`
* could result in an array of results (sync) or promises (async)
*/
private evalSyncAsync(val: unknown, cb: (unknown) => void) {
evalSyncAsync(val: unknown, cb: (unknown) => unknown): Promise<unknown> | unknown {
if (this.isAsync) {
return Promise.resolve(val).then(cb);
}
Expand Down
3 changes: 3 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ JseEval.addBinaryOp('~', 1, (a, b) => a * b);
JseEval.addBinaryOp('**', 11, true, (a, b) => a ** b);

JseEval.addEvaluator('TestNodeType', function(node) { return node.test + this.context.string });
JseEval.addEvaluator('TestNodeType2', function(node, context) { return node.test + context.string });

tape('sync', (t) => {
const syncFixtures = [
Expand All @@ -244,6 +245,7 @@ tape('sync', (t) => {

const val = JseEval.evaluate.bind(null, { type: 'TestNodeType', test: 'testing ' })(context);
t.equal(val, 'testing string');
t.equal(JseEval.evaluate.bind(null, { type: 'TestNodeType2', test: 'testing ' })(context), 'testing string');

t.end();
});
Expand Down Expand Up @@ -273,6 +275,7 @@ tape('async', async (t) => {

const val = await JseEval.evalAsync.bind(null, { type: 'TestNodeType', test: 'testing ' })(context);
t.equal(val, 'testing string');
t.equal(JseEval.evaluate.bind(null, { type: 'TestNodeType2', test: 'testing ' })(context), 'testing string');

t.end();
});
Expand Down

0 comments on commit 49a857f

Please sign in to comment.