0

Let's say I want to make a set of logic rules stored in an object :

const rule1 : Rule = {
  ">" : [3,2]
}; // Which represents : 3>2

const rule2 : Rule = {
  "and" : [
    { ">" : [3,1] },
    { "<" : [1,3] }
  ]
}; // Which represents : (3>1) && (1<3)

I have written my types as such :

type Operand = number | string | Rule

type Operator =
  "var" |
  "===" |
  "!==" |
  "<="  |
  "<"   |
  ">="  |
  ">"   |
  "or"  |
  "and" ;

interface Rule extends Record<Operator, [Operand, Operand]> { }

But I get the following error Type '{ ">": [number, number]; }' is missing the following properties from type 'Record<Operator, [Operand, Operand]>': var, "===", "!==", "<=", and 4 more.

What do I do wrong ?

3 Answers 3

1

interface Rule extends Record<Operator, [Operand, Operand]> { } will name all the operators required on any given rule.

You can extend Partial<Record<Operator, [Operand, Operand]>> and make them all optional. This would mean that a rule can specify multiple operators, which might not be what you want:

interface Rule extends Partial<Record<Operator, [Operand, Operand]>> { }
const rule1 : Rule = {
  ">" : [3, 2],
  "<" : [3, 2]
};

Playground Link

You could also not use an interface, but instead generate a union where each object type has one operator required:

type Rule<T extends Operator = Operator> =  T extends T ? Record<T, [Operand, Operand]> & Partial<Record<Exclude<Operator, T>, undefined>>: never;

Playground Link

With this second solution Rule basically generates a union of types using the distributive property of conditional types. Basically we get:

Rule = Record<"var", [Operand, Operand]> | Record<"===", [Operand, Operand]> | Record<"!==", [Operand, Operand]> | ... 5 more ... | Record<...>

This means that each constituent of the union has exactly one required property. To also ensure other don't have other properties (unions can allow members from other constituents), we also add to each object type all the other properties but optional and with type undefined (the & Partial<Record<Exclude<Operator, T>, undefined>> part)

Sign up to request clarification or add additional context in comments.

2 Comments

The first one is limpid but you are right I don't want multiple operator. The second one is cryptic and I 'm not sure to understand what's going on
@brospars added some explanations and changed the second version a bit since the original was not actually forbidding excess properties either
1

Maybe you will enjoy this simplified representation -

const rule1 : Expression =
  [ ">", 3, 2 ]

const rule2 : Expression =
  [ "and", [">", 3, 1], ["<", 1, 3] ] 

type Expression = 
  | number
  | string
  | [Operator, Expression, Expression]

type Operator =
  | "var"
  | "==="
  | "!=="
  | "<=" 
  | "<"  
  | ">=" 
  | ">"  
  | "or" 
  | "and"

Consider separating UnaryOp, BinaryOp, TernaryOp, etc -

const rule1 : Expression =
  [ ">", 3, 2 ]

const rule2 : Expression =
  [ "and", [">", 3, 1], ["<", 1, 3] ] 

const rule3 : Expression =
  [ "?:", [">", 3, 2], ["+", 10, 20], ["not", true]]

type Expression = 
  | number
  | string
  | boolean
  | [UnaryOp, Expression]
  | [BinaryOp, Expression, Expression]
  | [TernaryOp, Expression, Expression, Expression]

type UnaryOp =
  | "not"

type BinaryOp =
  | "var"
  | "==="
  | "!=="
  | "<=" 
  | "<"  
  | ">=" 
  | ">"  
  | "or" 
  | "and"
  | "+"
  | "-"

type TernaryOp =
  | "?:"

Comments

0

I have a simpler answer than the one provided by @Titian that doesn't require any Typescript black magic (distributive property of conditional types)

type Operators = "var" | ">" | ">=" | "<" | "<=" | "===" | "!==" | "and" | "or";
type Operand = number | string | Rule;
type Operands = [Operand, Operand?];

type Rule = {
  [key in Operators]?: Operands; // note the '?' that makes every properties of Rule not required
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.