From 1cfa1f6a49d1f2e3e512a74ab611c82c5c2db773 Mon Sep 17 00:00:00 2001 From: Paul Visscher Date: Sun, 19 May 2024 22:44:53 +0200 Subject: [PATCH] refactor: remove tree type inferrence --- src/engine/operator.ts | 27 ++++++------ src/engine/policy.ts | 24 ++++------- src/engine/types.spec.ts | 4 +- src/engine/types.ts | 17 +++++--- src/expressions/boolean.spec.ts | 57 ++++++++++++++++++------- src/expressions/boolean.ts | 10 ++--- src/expressions/higher-order-fn.spec.ts | 24 +++++++---- src/expressions/higher-order-fn.ts | 19 +++++++-- src/expressions/input.ts | 26 +++++------ src/expressions/logic.spec.ts | 28 +++++++++--- src/expressions/logic.ts | 4 +- src/expressions/number-array.spec.ts | 20 ++++++--- src/expressions/number.spec.ts | 42 +++++++++++------- src/expressions/string.spec.ts | 11 +++-- test/policies.spec.ts | 5 ++- 15 files changed, 198 insertions(+), 120 deletions(-) diff --git a/src/engine/operator.ts b/src/engine/operator.ts index bcfd72a..11a091a 100644 --- a/src/engine/operator.ts +++ b/src/engine/operator.ts @@ -1,9 +1,9 @@ +import { inspect } from 'node:util' +import type { JSONExpr } from '../json/jsonexpr.type.js' import type { AsExpression, DefinitionType, Expression, ValueExpression } from './types.js' import { fromLiteral } from './types.js' -import type { JSONExpr } from '../json/jsonexpr.type.js' - -import { inspect } from 'node:util' +export type FactsFomExprs = T extends { facts: infer U } ? U : never // biome-ignore lint/suspicious/noExplicitAny: any is necessary for the operator function export interface Operator { @@ -11,8 +11,13 @@ export interface Operator { operator: Op | I[K] }>( ...exprs: Exprs + ): ValueExpression< + O, + { [K in keyof I]: AsExpression }, + FactsFomExprs, // biome-ignore lint/suspicious/noExplicitAny: any is necessary for the operator function - ): ValueExpression }, Extract>> + Extract> + > } export function operator({ @@ -25,10 +30,7 @@ export function operator({ symbol: string }): Operator { return Object.assign( - | I[K] }>( - ...exprs: Exprs - // biome-ignore lint/suspicious/noExplicitAny: any is necessary for the operator function - ): ValueExpression }, Extract>> => { + | I[K] }>(...exprs: Exprs) => { const xs = exprs.map((x) => fromLiteral(x as I[number])) return { fn, @@ -39,16 +41,11 @@ export function operator({ [inspect.custom]() { return `${symbol}(${xs.map((x) => x[inspect.custom]?.() ?? '').join(', ')})` }, - } as unknown as ValueExpression< - O, - { [K in keyof I]: AsExpression }, - // biome-ignore lint/suspicious/noExplicitAny: any is necessary for the operator function - Extract> - > + } }, { symbol, operator, }, - ) + ) as unknown as Operator } diff --git a/src/engine/policy.ts b/src/engine/policy.ts index febd67e..0e068f2 100644 --- a/src/engine/policy.ts +++ b/src/engine/policy.ts @@ -1,6 +1,7 @@ import { collect, stack } from '@skyleague/axioms' -import type { Simplify, UnionToIntersection } from '@skyleague/axioms/types' +import type { IsEmptyObject, Simplify } from '@skyleague/axioms/types' import { version } from '../../package.json' +import type { FactsFomExprs } from './operator.js' import type { Expression, ExpressionReturnType } from './types.js' export class EvaluationContext { @@ -73,23 +74,18 @@ function* collapseExpression(root: Expression[], seen = new WeakSet()) { type InferFactName = Expr extends { name: string } ? Expr['name'] : k -type FilterFactExpressions, k extends keyof Facts> = Facts[k] extends { +type FilterFactExpressions = Facts[K] extends { _type: 'fact' } - ? InferFactName + ? InferFactName : never -type CollapseTreeToArray = T extends { dependsOn: (infer U)[] } ? T | CollapseTreeToArray : T -type _InputFromExpressions> = Simplify<{ - [k in keyof Facts as FilterFactExpressions]: ExpressionReturnType +type _InputFromExpressions = Simplify<{ + [K in keyof Facts as FilterFactExpressions]: ExpressionReturnType }> -type NamedUnionToRecord = T extends { name: infer Name } ? (Name extends PropertyKey ? { [k in Name]: T } : never) : never - +type _FactsFomExprs = Facts extends unknown[] ? Facts[number] : Facts export type InputFromExpressions> = Simplify< - _InputFromExpressions & - _InputFromExpressions< - UnionToIntersection ? CollapseTreeToArray : never>> - > + _InputFromExpressions & _InputFromExpressions<{ [K in keyof Facts]: _FactsFomExprs> }> > export type OutputFromFacts> = Simplify<{ @@ -97,13 +93,12 @@ export type OutputFromFacts> = Simplify<{ }> export interface Policy { - evaluate: [I] extends [never] ? () => { input: I; output: O } : (x: I) => { input: I; output: O } + evaluate: IsEmptyObject extends true ? () => { input: I; output: O } : (x: I) => { input: I; output: O } expr: () => unknown } export function $policy>( expressions: Facts, - // @ts-ignore ): Policy, OutputFromFacts> { const facts = Object.entries(expressions).map(([name, e]) => { if (e.name === undefined) { @@ -122,7 +117,6 @@ export function $policy>( const properties = Object.fromEntries(inputNodes.map((e) => [e.name, e.expr('definition')])) const outputExpression = Object.fromEntries(outputNodes.map((f) => [f.name, f.expr('expression')])) return { - // @ts-ignore evaluate: ((input: Record) => { const ctx = new EvaluationContext(input) diff --git a/src/engine/types.spec.ts b/src/engine/types.spec.ts index d7d6d81..59db747 100644 --- a/src/engine/types.spec.ts +++ b/src/engine/types.spec.ts @@ -23,10 +23,10 @@ describe('fromLiteral', () => { const fact = $fact(Arithmetic, 'input') const fa = fromLiteral($from(fact, '$.a')) const _fa_: Expression = fa - expectTypeOf(fa).toEqualTypeOf]>>() + expectTypeOf(fa).toEqualTypeOf>>() const fb = fromLiteral($from(fact, '$.b')) const _fb_: Expression = fb - expectTypeOf(fb).toEqualTypeOf]>>() + expectTypeOf(fb).toEqualTypeOf>>() type val = ['1', '2'] const _test_multiple = {} as { [K in keyof val]: AsExpression } diff --git a/src/engine/types.ts b/src/engine/types.ts index fce02c3..c284b0d 100644 --- a/src/engine/types.ts +++ b/src/engine/types.ts @@ -1,6 +1,6 @@ import type { EvaluationContext } from './policy.js' -import { $literal } from '../expressions/input.js' +import { $literal, type Fact } from '../expressions/input.js' import type { BooleanArrExpr, BooleanExpr, @@ -40,6 +40,7 @@ export interface Expression O expr: (definition: DefinitionType) => Expr [inspect.custom]?(): string @@ -50,20 +51,23 @@ export type ExpressionReturnType = E extends Pick ? ReturnT // biome-ignore lint/suspicious/noExplicitAny: this is needed for greedy matching export interface LiteralExpression> extends Expression { dependsOn: [] + // facts: [] _type: 'literal' } -export interface FactExpression> extends Expression { +// biome-ignore lint/suspicious/noExplicitAny: this is needed for greedy matching +export interface FactExpression> extends Expression { _type: 'fact' } export interface InputExpression< O, I, - DependsOn extends Expression[], + F extends Fact, Expr extends JSONExpr | ValueItemExpr = InferExpressionType, > extends Expression { - dependsOn: DependsOn + dependsOn: [F] + facts: [F] _type: 'value' | 'literal' } @@ -71,9 +75,10 @@ export type InputFromExpressions = { [k in keyof Expr]: ExpressionReturnType } -export interface ValueExpression> +export interface ValueExpression> extends Expression, Expr> { - dependsOn: DependsOn + // dependsOn: DependsOn + facts: Facts //extends FactExpression ? [Facts] : never _type?: 'value' } diff --git a/src/expressions/boolean.spec.ts b/src/expressions/boolean.spec.ts index 84824da..d7cec74 100644 --- a/src/expressions/boolean.spec.ts +++ b/src/expressions/boolean.spec.ts @@ -90,14 +90,20 @@ describe('startsWith', () => { const x1 = $startsWith('1', '2') expectTypeOf(x1).toEqualTypeOf< - ValueExpression, LiteralExpression<'2', StringExpr>], StartsWithExpr> + ValueExpression< + boolean, + [LiteralExpression<'1', StringExpr>, LiteralExpression<'2', StringExpr>], + never, + StartsWithExpr + > >() const x2 = $startsWith($from(fact, '$.a'), '2') expectTypeOf(x2).toEqualTypeOf< ValueExpression< boolean, - [From]>, LiteralExpression<'2', StringExpr>], + [str: From>, searchString: LiteralExpression<'2', StringExpr>], + [Fact], StartsWithExpr > >() @@ -106,7 +112,8 @@ describe('startsWith', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< boolean, - [LiteralExpression<'2', StringExpr>, From]>], + [str: LiteralExpression<'2', StringExpr>, searchString: From>], + [Fact], StartsWithExpr > >() @@ -190,14 +197,20 @@ describe('endsWith', () => { const x1 = $endsWith('1', '2') expectTypeOf(x1).toEqualTypeOf< - ValueExpression, LiteralExpression<'2', StringExpr>], EndsWithExpr> + ValueExpression< + boolean, + [str: LiteralExpression<'1', StringExpr>, searchString: LiteralExpression<'2', StringExpr>], + never, + EndsWithExpr + > >() const x2 = $endsWith($from(fact, '$.a'), '2') expectTypeOf(x2).toEqualTypeOf< ValueExpression< boolean, - [From]>, LiteralExpression<'2', StringExpr>], + [str: From>, searchString: LiteralExpression<'2', StringExpr>], + [Fact], EndsWithExpr > >() @@ -206,7 +219,8 @@ describe('endsWith', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< boolean, - [LiteralExpression<'2', StringExpr>, From]>], + [str: LiteralExpression<'2', StringExpr>, searchString: From>], + [Fact], EndsWithExpr > >() @@ -290,14 +304,20 @@ describe('includes', () => { const x1 = $includes('1', '2') expectTypeOf(x1).toEqualTypeOf< - ValueExpression, LiteralExpression<'2', StringExpr>], IncludesExpr> + ValueExpression< + boolean, + [LiteralExpression<'1', StringExpr>, LiteralExpression<'2', StringExpr>], + never, + IncludesExpr + > >() const x2 = $includes($from(fact, '$.a'), '2') expectTypeOf(x2).toEqualTypeOf< ValueExpression< boolean, - [From]>, LiteralExpression<'2', StringExpr>], + [str: From>, searchString: LiteralExpression<'2', StringExpr>], + [Fact], IncludesExpr > >() @@ -306,7 +326,8 @@ describe('includes', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< boolean, - [LiteralExpression<'2', StringExpr>, From]>], + [str: LiteralExpression<'2', StringExpr>, searchString: From>], + [Fact], IncludesExpr > >() @@ -413,7 +434,9 @@ describe('all', () => { const fact = $fact(LogicObj, 'input') const x1 = $all([1, 2], (x) => $equal(x, 1)) - expectTypeOf(x1).toEqualTypeOf], BooleanExpr>>() + expectTypeOf(x1).toEqualTypeOf< + ValueExpression], never, BooleanExpr> + >() const a = $from(fact, '$.d') const x2 = $all(a, (x) => $equal(x, 1)) @@ -427,9 +450,10 @@ describe('all', () => { a: boolean b: boolean }[], - [Fact] + Fact >, ], + [Fact], BooleanExpr > >() @@ -437,7 +461,7 @@ describe('all', () => { const ba = $from(fact, '$.b..a') const x3 = $all(ba, (x) => $equal(x, 1)) expectTypeOf(x3).toEqualTypeOf< - ValueExpression]>], BooleanExpr> + ValueExpression>], [Fact], BooleanExpr> >() }) }) @@ -542,7 +566,9 @@ describe('any', () => { const fact = $fact(LogicObj, 'input') const x1 = $any([1, 2], (x) => $equal(x, 1)) - expectTypeOf(x1).toEqualTypeOf], BooleanExpr>>() + expectTypeOf(x1).toEqualTypeOf< + ValueExpression], never, BooleanExpr> + >() const a = $from(fact, '$.d') const x2 = $any(a, (x) => $equal(x, 1)) @@ -556,9 +582,10 @@ describe('any', () => { a: boolean b: boolean }[], - [Fact] + Fact >, ], + [Fact], BooleanExpr > >() @@ -566,7 +593,7 @@ describe('any', () => { const ba = $from(fact, '$.b..a') const x3 = $any(ba, (x) => $equal(x, 1)) expectTypeOf(x3).toEqualTypeOf< - ValueExpression]>], BooleanExpr> + ValueExpression>], [Fact], BooleanExpr> >() }) }) diff --git a/src/expressions/boolean.ts b/src/expressions/boolean.ts index d6a6a9d..07de8eb 100644 --- a/src/expressions/boolean.ts +++ b/src/expressions/boolean.ts @@ -1,6 +1,6 @@ import { $value, type ValueItem } from './higher-order-fn.js' -import { operator } from '../engine/operator.js' +import { type FactsFomExprs, operator } from '../engine/operator.js' import { type AsExpression, type Expression, @@ -44,7 +44,7 @@ export const $all = Object.assign( >( xs: Expr, predicate: (value: ValueItem>) => Expression, - ): ValueExpression]> => { + ): ValueExpression], FactsFomExprs> => { const _xs = fromLiteral(xs) const _value = $value>() const _transform = predicate(_value) @@ -59,7 +59,7 @@ export const $all = Object.assign( [inspect.custom]() { return `$all(${_xs[inspect.custom]?.() ?? ''}, (x) => ${_transform[inspect.custom]?.() ?? ''})` }, - } as ValueExpression]> + } as ValueExpression], FactsFomExprs> }, // biome-ignore lint/suspicious/noExplicitAny: this is needed for greedy matching { operator: 'all', symbol: '$all', parse: (xs: any, predicate: any) => $all(xs, () => predicate) } as const, @@ -70,7 +70,7 @@ export const $any = Object.assign( >( xs: Expr, predicate: (value: ValueItem>) => Expression, - ): ValueExpression]> => { + ): ValueExpression], FactsFomExprs> => { const _xs = fromLiteral(xs) const _value = $value>() const _transform = predicate(_value) @@ -85,7 +85,7 @@ export const $any = Object.assign( [inspect.custom]() { return `$any(${_xs[inspect.custom]?.() ?? ''}, (x) => ${_transform[inspect.custom]?.() ?? ''})` }, - } as ValueExpression]> + } as ValueExpression], FactsFomExprs> }, // biome-ignore lint/suspicious/noExplicitAny: this is needed for greedy matching { operator: 'any', symbol: '$any', parse: (xs: any, predicate: any) => $any(xs, () => predicate) } as const, diff --git a/src/expressions/higher-order-fn.spec.ts b/src/expressions/higher-order-fn.spec.ts index e21b573..b640bae 100644 --- a/src/expressions/higher-order-fn.spec.ts +++ b/src/expressions/higher-order-fn.spec.ts @@ -181,20 +181,26 @@ describe('map', () => { const fact = $fact(MathFn, 'input') const x1 = $map([1, 2], (x) => x) - expectTypeOf(x1).toEqualTypeOf], NumberExpr>>() + expectTypeOf(x1).toEqualTypeOf< + ValueExpression], never, NumberExpr> + >() const a = $from(fact, '$.a') const x2 = $map(a, (x) => x) - expectTypeOf(x2).toEqualTypeOf]>], NumberExpr>>() + expectTypeOf(x2).toEqualTypeOf< + ValueExpression>], [Fact], NumberExpr> + >() const ba = $from(fact, '$.b..a') const x3 = $map(ba, (x) => x) - expectTypeOf(x3).toEqualTypeOf]>], NumberExpr>>() + expectTypeOf(x3).toEqualTypeOf< + ValueExpression>], [Fact], NumberExpr> + >() const b = $from(fact, '$.b') const x4 = $map(b, (x) => x('$.a')) expectTypeOf(x4).toEqualTypeOf< - ValueExpression]>], NumberExpr> + ValueExpression>], [Fact], NumberExpr> >() }) }) @@ -285,24 +291,26 @@ describe('filter', () => { const fact = $fact(MathFn, 'input') const x1 = $filter([1, 2], (x) => $equal(x, 1)) - expectTypeOf(x1).toEqualTypeOf], NumberArrExpr>>() + expectTypeOf(x1).toEqualTypeOf< + ValueExpression], never, NumberArrExpr> + >() const a = $from(fact, '$.a') const x2 = $filter(a, (x) => $equal(x, 1)) expectTypeOf(x2).toEqualTypeOf< - ValueExpression]>], NumberArrExpr> + ValueExpression>], [Fact], NumberArrExpr> >() const ba = $from(fact, '$.b..a') const x3 = $filter(ba, (x) => $equal(x, 1)) expectTypeOf(x3).toEqualTypeOf< - ValueExpression]>], NumberArrExpr> + ValueExpression>], [Fact], NumberArrExpr> >() const b = $from(fact, '$.b') const x4 = $filter(b, (x) => $equal(1, x('$.a'))) expectTypeOf(x4).toEqualTypeOf< - ValueExpression]>], JSONExpr> + ValueExpression>], [Fact], JSONExpr> >() }) }) diff --git a/src/expressions/higher-order-fn.ts b/src/expressions/higher-order-fn.ts index 9c138fe..f1ea816 100644 --- a/src/expressions/higher-order-fn.ts +++ b/src/expressions/higher-order-fn.ts @@ -1,3 +1,4 @@ +import type { FactsFomExprs } from '../engine/operator.js' import type { EvaluationContext } from '../engine/policy.js' import { type AsExpression, @@ -16,7 +17,7 @@ import { JSONPath, type JSONPathValue } from '@skyleague/jsonpath' import { inspect } from 'node:util' // biome-ignore lint/suspicious/noExplicitAny: this is needed for greedy matching -export interface Value extends InputExpression { +export interface Value extends InputExpression { _type: 'value' } function value(path?: P): P extends string ? Value> : Value { @@ -62,7 +63,12 @@ export const $map = Object.assign( , Expr extends LiteralOr>( xs: Expr, transform: (value: ValueItem>) => AsExpression, - ): ValueExpression[], [AsExpression], InferExpressionType>> => { + ): ValueExpression< + ExpressionTypeOfLiteral[], + [AsExpression], + FactsFomExprs, + InferExpressionType> + > => { const _xs = fromLiteral(xs) const _value = $value>() const _transform = transform(_value) @@ -77,7 +83,12 @@ export const $map = Object.assign( [inspect.custom]() { return `$map(${_xs[inspect.custom]?.() ?? ''}, (x) => ${_transform[inspect.custom]?.() ?? ''})` }, - } as ValueExpression[], [AsExpression], InferExpressionType>> + } as ValueExpression< + ExpressionTypeOfLiteral[], + [AsExpression], + FactsFomExprs, + InferExpressionType> + > }, // biome-ignore lint/suspicious/noExplicitAny: this is needed for greedy matching { operator: 'map', symbol: '$map', parse: (xs: any, transform: any) => $map(xs, () => transform) } as const, @@ -91,6 +102,7 @@ export const $filter = Object.assign( ): ValueExpression< ExpressionTypeOfLiteral, [AsExpression], + FactsFomExprs, InferExpressionType> > => { const _xs = fromLiteral(xs) @@ -110,6 +122,7 @@ export const $filter = Object.assign( } as ValueExpression< ExpressionTypeOfLiteral, [AsExpression], + FactsFomExprs, InferExpressionType> > }, diff --git a/src/expressions/input.ts b/src/expressions/input.ts index d11c9f8..41d130f 100644 --- a/src/expressions/input.ts +++ b/src/expressions/input.ts @@ -1,11 +1,4 @@ -import type { - DefinitionType, - Expression, - FactExpression, - InferExpressionType, - InputExpression, - LiteralExpression, -} from '../engine/types.js' +import type { DefinitionType, FactExpression, InferExpressionType, InputExpression, LiteralExpression } from '../engine/types.js' import type { FromExpr } from '../json/jsonexpr.type.js' import { JSONPath, type JSONPathValue } from '@skyleague/jsonpath' @@ -13,7 +6,8 @@ import type { Schema } from '@skyleague/therefore' import { inspect } from 'node:util' -export interface Fact extends FactExpression { +// biome-ignore lint/suspicious/noExplicitAny: this is needed for greedy matching +export interface Fact extends FactExpression { name: Name dependsOn: [] _type: 'fact' @@ -34,32 +28,32 @@ export function $fact(schema: Pick, 'schema' | }, } } -export interface From extends InputExpression { +export interface From extends InputExpression { _type: 'value' } -export function $from(fact: Fact): From, [Fact]> +export function $from(fact: Fact): From, Fact> export function $from( fact: Fact, path: P, -): From, [Fact]> +): From, Fact> export function $from( fact: Fact, path: P = '$' as P, -): From, [Fact]> { +): From, Fact> { return { _type: 'value', dependsOn: [fact], fn: ((_: unknown, ctx: { input: Record }) => JSONPath.get(ctx.input[fact.name], path)) as From< T, JSONPathValue, - [Fact] + Fact >['fn'], - expr: (_definition) => ({ from: [fact.name, path.toString()] as const }), + expr: () => ({ from: [fact.name, path.toString()] as const }), [inspect.custom]() { return `$from(${fact[inspect.custom]?.() ?? `"${fact.name}"`}${path === '$' ? '' : `, "${path}"`})` }, - } + } as unknown as From, Fact> } export function $literal(x: O): LiteralExpression { diff --git a/src/expressions/logic.spec.ts b/src/expressions/logic.spec.ts index 38b330e..26624d9 100644 --- a/src/expressions/logic.spec.ts +++ b/src/expressions/logic.spec.ts @@ -76,6 +76,7 @@ describe('if', () => { LiteralExpression, LiteralExpression, ], + never, IfExpr > >() @@ -89,6 +90,7 @@ describe('if', () => { LiteralExpression, LiteralExpression, ], + never, IfExpr > >() @@ -154,14 +156,20 @@ describe('and', () => { const x1 = $and(false, true, false) expectTypeOf(x1).toEqualTypeOf< - ValueExpression | LiteralExpression)[], AndExpr> + ValueExpression< + boolean, + (LiteralExpression | LiteralExpression)[], + never, + AndExpr + > >() const x2 = $and($from(fact, '$.c'), true) expectTypeOf(x2).toEqualTypeOf< ValueExpression< boolean, - (LiteralExpression | From]>)[], + (LiteralExpression | From>)[], + [Fact], AndExpr > >() @@ -170,7 +178,8 @@ describe('and', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< boolean, - (LiteralExpression | From]>)[], + (From> | LiteralExpression)[], + [Fact], AndExpr > >() @@ -236,14 +245,20 @@ describe('or', () => { const x1 = $or(false, true, false) expectTypeOf(x1).toEqualTypeOf< - ValueExpression | LiteralExpression)[], OrExpr> + ValueExpression< + boolean, + (LiteralExpression | LiteralExpression)[], + never, + OrExpr + > >() const x2 = $or($from(fact, '$.c'), true) expectTypeOf(x2).toEqualTypeOf< ValueExpression< boolean, - (LiteralExpression | From]>)[], + (From> | LiteralExpression)[], + [Fact], OrExpr > >() @@ -252,7 +267,8 @@ describe('or', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< boolean, - (LiteralExpression | From]>)[], + (From> | LiteralExpression)[], + [Fact], OrExpr > >() diff --git a/src/expressions/logic.ts b/src/expressions/logic.ts index 8234604..4735171 100644 --- a/src/expressions/logic.ts +++ b/src/expressions/logic.ts @@ -1,4 +1,4 @@ -import { operator } from '../engine/operator.js' +import { type FactsFomExprs, operator } from '../engine/operator.js' import { type AsExpression, type Expression, @@ -18,6 +18,7 @@ export const $if = Object.assign( ): ValueExpression< ExpressionTypeOfLiteral | ExpressionTypeOfLiteral, [AsExpression, AsExpression, AsExpression], + FactsFomExprs, IfExpr > => { const condition = fromLiteral(_condition) @@ -29,6 +30,7 @@ export const $if = Object.assign( expr: (mod) => ({ if: [condition.expr(mod), a.expr(mod), b.expr(mod)], }), + facts: undefined as never, } }, { operator: 'if', symbol: '$if' }, diff --git a/src/expressions/number-array.spec.ts b/src/expressions/number-array.spec.ts index eee34a1..5ff90bf 100644 --- a/src/expressions/number-array.spec.ts +++ b/src/expressions/number-array.spec.ts @@ -99,15 +99,19 @@ describe('min', () => { const fact = $fact(MathFn, 'input') const x1 = $min([1, 2]) - expectTypeOf(x1).toEqualTypeOf], MinExpr>>() + expectTypeOf(x1).toEqualTypeOf], never, MinExpr>>() const a = $from(fact, '$.a') const x2 = $min(a) - expectTypeOf(x2).toEqualTypeOf]>], MinExpr>>() + expectTypeOf(x2).toEqualTypeOf< + ValueExpression>], [Fact], MinExpr> + >() const b = $from(fact, '$.b..a') const x3 = $min(b) - expectTypeOf(x3).toEqualTypeOf]>], MinExpr>>() + expectTypeOf(x3).toEqualTypeOf< + ValueExpression>], [Fact], MinExpr> + >() }) }) @@ -198,14 +202,18 @@ describe('max', () => { const fact = $fact(MathFn, 'input') const x1 = $max([1, 2]) - expectTypeOf(x1).toEqualTypeOf], MaxExpr>>() + expectTypeOf(x1).toEqualTypeOf], never, MaxExpr>>() const a = $from(fact, '$.a') const x2 = $max(a) - expectTypeOf(x2).toEqualTypeOf]>], MaxExpr>>() + expectTypeOf(x2).toEqualTypeOf< + ValueExpression>], [Fact], MaxExpr> + >() const b = $from(fact, '$.b..a') const x3 = $max(b) - expectTypeOf(x3).toEqualTypeOf]>], MaxExpr>>() + expectTypeOf(x3).toEqualTypeOf< + ValueExpression>], [Fact], MaxExpr> + >() }) }) diff --git a/src/expressions/number.spec.ts b/src/expressions/number.spec.ts index b035967..8f0cdda 100644 --- a/src/expressions/number.spec.ts +++ b/src/expressions/number.spec.ts @@ -88,14 +88,15 @@ describe('add', () => { const x1 = $add(1, 2) expectTypeOf(x1).toEqualTypeOf< - ValueExpression, LiteralExpression<2, NumberExpr>], AddExpr> + ValueExpression, LiteralExpression<2, NumberExpr>], never, AddExpr> >() const x2 = $add($from(fact, '$.a'), 2) expectTypeOf(x2).toEqualTypeOf< ValueExpression< number, - [From]>, LiteralExpression<2, NumberExpr>], + [From>, LiteralExpression<2, NumberExpr>], + [Fact], AddExpr > >() @@ -104,7 +105,8 @@ describe('add', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< number, - [LiteralExpression<2, NumberExpr>, From]>], + [LiteralExpression<2, NumberExpr>, From>], + [Fact], AddExpr > >() @@ -181,7 +183,7 @@ describe('subtract', () => { const result = subs.evaluate() expectTypeOf(x1).toEqualTypeOf< - ValueExpression, LiteralExpression], SubExpr> + ValueExpression, LiteralExpression], never, SubExpr> >() expect(result.output.x1).toEqual(a - b) }) @@ -192,14 +194,15 @@ describe('subtract', () => { const x1 = $subtract(1, 2) expectTypeOf(x1).toEqualTypeOf< - ValueExpression, LiteralExpression<2, NumberExpr>], SubExpr> + ValueExpression, LiteralExpression<2, NumberExpr>], never, SubExpr> >() const x2 = $subtract($from(fact, '$.a'), 2) expectTypeOf(x2).toEqualTypeOf< ValueExpression< number, - [From]>, LiteralExpression<2, NumberExpr>], + [From>, LiteralExpression<2, NumberExpr>], + [Fact], SubExpr > >() @@ -208,7 +211,8 @@ describe('subtract', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< number, - [LiteralExpression<2, NumberExpr>, From]>], + [LiteralExpression<2, NumberExpr>, From>], + [Fact], SubExpr > >() @@ -292,14 +296,15 @@ describe('multiply', () => { const x1 = $multiply(1, 2) expectTypeOf(x1).toEqualTypeOf< - ValueExpression, LiteralExpression<2, NumberExpr>], MulExpr> + ValueExpression, LiteralExpression<2, NumberExpr>], never, MulExpr> >() const x2 = $multiply($from(fact, '$.a'), 2) expectTypeOf(x2).toEqualTypeOf< ValueExpression< number, - [From]>, LiteralExpression<2, NumberExpr>], + [From>, LiteralExpression<2, NumberExpr>], + [Fact], MulExpr > >() @@ -308,7 +313,8 @@ describe('multiply', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< number, - [LiteralExpression<2, NumberExpr>, From]>], + [LiteralExpression<2, NumberExpr>, From>], + [Fact], MulExpr > >() @@ -391,14 +397,15 @@ describe('divide', () => { const x1 = $divide(1, 2) expectTypeOf(x1).toEqualTypeOf< - ValueExpression, LiteralExpression<2, NumberExpr>], DivExpr> + ValueExpression, LiteralExpression<2, NumberExpr>], never, DivExpr> >() const x2 = $divide($from(fact, '$.a'), 2) expectTypeOf(x2).toEqualTypeOf< ValueExpression< number, - [From]>, LiteralExpression<2, NumberExpr>], + [From>, LiteralExpression<2, NumberExpr>], + [Fact], DivExpr > >() @@ -407,7 +414,8 @@ describe('divide', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< number, - [LiteralExpression<2, NumberExpr>, From]>], + [LiteralExpression<2, NumberExpr>, From>], + [Fact], DivExpr > >() @@ -491,14 +499,15 @@ describe('mod', () => { const x1 = $modulo(1, 2) expectTypeOf(x1).toEqualTypeOf< - ValueExpression, LiteralExpression<2, NumberExpr>], ModExpr> + ValueExpression, LiteralExpression<2, NumberExpr>], never, ModExpr> >() const x2 = $modulo($from(fact, '$.a'), 2) expectTypeOf(x2).toEqualTypeOf< ValueExpression< number, - [From]>, LiteralExpression<2, NumberExpr>], + [From>, LiteralExpression<2, NumberExpr>], + [Fact], ModExpr > >() @@ -507,7 +516,8 @@ describe('mod', () => { expectTypeOf(x3).toEqualTypeOf< ValueExpression< number, - [LiteralExpression<2, NumberExpr>, From]>], + [LiteralExpression<2, NumberExpr>, From>], + [Fact], ModExpr > >() diff --git a/src/expressions/string.spec.ts b/src/expressions/string.spec.ts index f2d3c5f..4fcdd99 100644 --- a/src/expressions/string.spec.ts +++ b/src/expressions/string.spec.ts @@ -97,6 +97,7 @@ describe('concat', () => { // these duplicates are not the nicest ...(LiteralExpression<'1', StringExpr> | LiteralExpression<'2', StringExpr>)[], ], + never, ConcatExpr > >() @@ -106,11 +107,12 @@ describe('concat', () => { ValueExpression< string, [ - From]>, + From>, LiteralExpression<'2', StringExpr>, // these duplicates are not the nicest - ...(From]> | LiteralExpression<'2', StringExpr>)[], + ...(From> | LiteralExpression<'2', StringExpr>)[], ], + [Fact], ConcatExpr > >() @@ -121,10 +123,11 @@ describe('concat', () => { string, [ LiteralExpression<'2', StringExpr>, - From]>, + From>, // these duplicates are not the nicest - ...(From]> | LiteralExpression<'2', StringExpr>)[], + ...(From> | LiteralExpression<'2', StringExpr>)[], ], + [Fact], ConcatExpr > >() diff --git a/test/policies.spec.ts b/test/policies.spec.ts index bb791e5..3b2cd9b 100644 --- a/test/policies.spec.ts +++ b/test/policies.spec.ts @@ -33,8 +33,9 @@ describe('arbitrary', () => { describe('any string starts with', () => { const inputs = ['foo', 'bar', 'baz'] - const startedWith: ValueExpression], BooleanExpr> = $map(inputs, (x) => - $startsWith(x, 'foo'), + const startedWith: ValueExpression], never, BooleanExpr> = $map( + inputs, + (x) => $startsWith(x, 'foo'), ) const startsWithFoo = $policy({ startedWith })