Can't correctly type JSON logic with a dynamic operator

171 views Asked by At

I'm using the definitely typed package for json-logic-js.

I can't seem to get TypeScript to compile when I'm using a dynamic operator, but it works fine if I use a static one. This works:

const staticRule: RulesLogic = {
  '==': [{var:'one'},1]
}

but this doesn't:

const op: ReservedOperations[] = ['==','>=','<=','===']

const dynamicOperator = op[1]

const dynamicRule: RulesLogic = {
  [dynamicOperator]: [{var:'one'},1]
}

I get the error:

Type '{ [x: string]: (number | { var: string; })[]; }' is not assignable to type 'RulesLogic<never>'.
  Property 'log' is missing in type '{ [x: string]: (number | { var: string; })[]; }' but required in type 'JsonLogicLog<never>'.

Here's a TypeScript playground.


Even if I specify just both those operators it still doesn't work:

const createRulesLogic = (operation: '==' | '!='): RulesLogic => ({
  [operation]: [{var:'one'},1]
})

Results in the error:

Type '{ [x: string]: (number | { var: string; })[]; }' is not assignable to type 'RulesLogic<never>'.
  Property 'log' is missing in type '{ [x: string]: (number | { var: string; })[]; }' but required in type 'JsonLogicLog<never>'.

Playground link

2

There are 2 answers

1
Alberto Fecchi On

staticRule is automatically recognized as a valid RulesLogic, in particular as JsonLogicEqual due to the fact that you explicit set the operator/ket to ==.

The "dynamic" one doesn't know in advance which operator will you use and warns you that, if the dynamic operator would be a log, your rule will be missing the log key inside the object.

0
Hedscan On

It looks to me like you've reached the edge of what TS can reason about at compile-time:

const enabledOps = [
    '==', '!='
] as const satisfies readonly ReservedOperation[]

const createLogic1 = (operation: typeof enabledOps[1]): RulesLogic => ({
    [operation]: [{var: 'one'}, 1]
})
// ^ type-checks

const createLogic0or1 = (operation: typeof enabledOps[0 | 1]): RulesLogic => ({
    [operation]: [{var: 'one'}, 1]
})
// ^ this errors, but absolutely shouldn't

Congratulations!