Skip to content

Commit 49a857f

Browse files
authored
fix: update evaluatorCallback types (this, and access to class properties) (#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
1 parent cf29567 commit 49a857f

File tree

3 files changed

+24
-14
lines changed

3 files changed

+24
-14
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,13 @@ expr.addEvaluator('TestNodeType', function(node) {
198198
});
199199
console.log(expr.eval({ type: 'TestNodeType', test: 'testing ' }, { string: 'jse-eval' })); // 'testing jse-eval'
200200

201+
// override default implementation:
202+
expr.addEvaluator('Identifier', function myIdentifier(node: Identifier) {
203+
return context?.[node.name];
204+
});
205+
console.log(expr.eval({ type: 'Identifier', name: 'x' }, { x: 'jse-eval' })); // 'jse-eval'
206+
207+
201208
const myPlugin = {
202209
name: 'Exponentiation',
203210
init(jsep) {

index.ts

+14-14
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ import jsep from 'jsep';
1212
* Copyright (c) 2013 Stephen Oney, http://jsep.from.so/
1313
*/
1414

15-
declare type Context = Record<string, unknown>;
16-
declare type operand = any;
17-
declare type unaryCallback = (a: operand) => operand;
18-
declare type binaryCallback = (a: operand, b: operand) => operand;
19-
declare type assignCallback = (obj: Record<string, operand>, key: string, val: operand) => operand;
20-
declare type evaluatorCallback = (node: AnyExpression, context: Context) => unknown;
15+
export declare type Context = Record<string, unknown>;
16+
export declare type operand = any;
17+
export declare type unaryCallback = (a: operand) => operand;
18+
export declare type binaryCallback = (a: operand, b: operand) => operand;
19+
export declare type assignCallback = (obj: Record<string, operand>, key: string, val: operand) => operand;
20+
export declare type evaluatorCallback<T extends AnyExpression> = (this: ExpressionEval, node: T, context?: Context) => unknown;
2121

22-
type AnyExpression = jsep.Expression;
22+
export type AnyExpression = jsep.Expression;
2323

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

@@ -30,7 +30,7 @@ export default class ExpressionEval {
3030
static parse = jsep;
3131
static evaluate = ExpressionEval.eval;
3232

33-
static evaluators: Record<string, evaluatorCallback> = {
33+
static evaluators: Record<string, evaluatorCallback<AnyExpression>> = {
3434
'ArrayExpression': ExpressionEval.prototype.evalArrayExpression,
3535
'LogicalExpression': ExpressionEval.prototype.evalBinaryExpression,
3636
'BinaryExpression': ExpressionEval.prototype.evalBinaryExpression,
@@ -157,7 +157,7 @@ export default class ExpressionEval {
157157
}
158158

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

@@ -197,8 +197,8 @@ export default class ExpressionEval {
197197
}
198198

199199

200-
protected context?: Context;
201-
protected isAsync?: boolean;
200+
context?: Context;
201+
isAsync?: boolean;
202202

203203
constructor(context?: Context, isAsync?: boolean) {
204204
this.context = context;
@@ -211,7 +211,7 @@ export default class ExpressionEval {
211211
if (!evaluator) {
212212
throw new Error(`unknown node type: ${JSON.stringify(node, null, 2)}`);
213213
}
214-
return this.evalSyncAsync(evaluator.bind(this)(node), (v) => {
214+
return this.evalSyncAsync(evaluator.bind(this)(node, this.context), (v) => {
215215
(node as any)._value = v;
216216
return cb(v);
217217
});
@@ -233,7 +233,7 @@ export default class ExpressionEval {
233233
* `promisesOrResults = expressions.map(v => this.eval(v))`
234234
* could result in an array of results (sync) or promises (async)
235235
*/
236-
private evalSyncAsync(val: unknown, cb: (unknown) => void) {
236+
evalSyncAsync(val: unknown, cb: (unknown) => unknown): Promise<unknown> | unknown {
237237
if (this.isAsync) {
238238
return Promise.resolve(val).then(cb);
239239
}

test.js

+3
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ JseEval.addBinaryOp('~', 1, (a, b) => a * b);
219219
JseEval.addBinaryOp('**', 11, true, (a, b) => a ** b);
220220

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

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

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

248250
t.end();
249251
});
@@ -273,6 +275,7 @@ tape('async', async (t) => {
273275

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

277280
t.end();
278281
});

0 commit comments

Comments
 (0)