2 votes

TypeScript : Typage récursif

J'ai une requête que je génère côté client en fonction de la saisie de l'utilisateur et qui ressemble à ceci.

const query = {
  "or": [
    {
      "field": "username",
      "operator": "in",
      "value": [
        "jdoe",
        "jsmith"
      ]
    },
    {
      "and": [
        {
          "field": "email",
          "operator": "matches",
          "value": "/^gmail.com/"
        },
        {
          "or": [
            {
              "field": "last_sign_in",
              "operator": "lt",
              "value": 1599619454323
            },
            {
              "field": "last_sign_in",
              "operator": "gt",
              "value": 1489613454395
            }
          ]
        }
      ]
    }
  ]
}

Cependant, dans un effort de migration vers une représentation typographique succincte, j'ai du mal à la faire fonctionner comme je le souhaite.

J'ai ces définitions :

type Operator = 'eq' | 'in' | 'matches' | 'lt' | 'gt';
type Condition = 'and' | 'or' | 'not';

interface SimpleQuery {
  field: string;
  operator: Operator;
  value: any;
}

interface Query {
  condition: SimpleQuery[] // here I want `condition` to come from the type Condition
// I have tried these solutions involving [{ x of y }] https://github.com/microsoft/TypeScript/issues/24220
}

Voici les erreurs que j'obtiens du compilateur TS :

A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.
A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
Cannot find name 'key'.
'Condition' only refers to a type, but is being used as a value here.

J'ai essayé avec

type Query = {
    [key in Condition]: SimpleQuery[];
}

Avec cette approche, Typescript veut que j'ajoute aussi toutes les conditions manquantes.

3voto

devilmaster Points 439

Je pense que ce serait le type le plus précis pour décrire les objets que vous avez décrits : :

type Operator = 'eq' | 'in' | 'matches' | 'lt' | 'gt';

type UnionKeys<T> = T extends T ? keyof T : never;
type Condition = UnionKeys<OperatorExpression>;

interface FieldCondition {
  field: string;
  operator: Operator;
  value: any;
}

type BinaryExpression<T extends PropertyKey> = {
    [P in T] : [FieldCondition | OperatorExpression, FieldCondition | OperatorExpression]
}
type UnaryExpression<T extends PropertyKey> = {
    [P in T] : [FieldCondition | OperatorExpression]
}

type OperatorExpression = BinaryExpression<"and"> | BinaryExpression<"or">  | UnaryExpression<"not"> 

const query: OperatorExpression = {
  "or": [
    {
      "field": "username",
      "operator": "in",
      "value": [
        "jdoe",
        "jsmith"
      ]
    },
    {
      "and": [
        {
          "field": "email",
          "operator": "matches",
          "value": "/^gmail.com/"
        },
        {
          "or": [
            {
              "field": "last_sign_in",
              "operator": "lt",
              "value": 1599619454323
            },
            {
              "field": "last_sign_in",
              "operator": "gt",
              "value": 1489613454395
            }
          ]
        }
      ]
    }
  ]
}

Lien vers le terrain de jeux

Cette version renforce l'arité correcte des opérateurs logiques (en utilisant des types de tuple) et a dérivé la fonction Condition union basé sur l'opérateur union à la place.

1voto

Michael Xu Points 324

Est-ce l'interface de requête que vous souhaitez ? Elle fonctionnera sur votre query mais je ne suis pas sûr que ce soit le niveau de détail que vous souhaitez...

interface Query {
   [key:string]: (SimpleQuery | Query)[]; 
}

1voto

satanTime Points 8307

Je pense que vous devez faire une récursion ici ( terrain de jeux ):

type Operator = 'eq' | 'in' | 'matches' | 'lt' | 'gt';
type Condition = 'and' | 'or' | 'not';

interface SimpleQuery {
  field: string;
  operator: Operator;
  value: any;
}

type Query = {
  [key in Condition]: Array<SimpleQuery | Query>;
}

const query: Query = {
  "or": [
    {
      "field": "username",
      "operator": "in",
      "value": [
        "jdoe",
        "jsmith"
      ]
    },
    {
      "and": [
        {
          "field": "email",
          "operator": "matches",
          "value": "/^gmail.com/"
        },
        {
          "or": [
            {
              "field": "last_sign_in",
              "operator": "lt",
              "value": 1599619454323
            },
            {
              "field": "last_sign_in",
              "operator": "gt",
              "value": 1489613454395
            }
          ]
        }
      ]
    }
  ]
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X