Partager via


Types

Une valeur de type est une valeur qui classifie d’autres valeurs. Une valeur classifiée par un type est dite conforme à ce type. Le système de type M se compose des genres de types suivants :

  • Types primitifs, qui classifient les valeurs primitives (binary, date, datetime, datetimezone, duration, list, logical, null, number, record, text, time, type) et incluent également un certain nombre de types abstraits (function, table, any, anynonnull et none)

  • Types d’enregistrements, qui classifient les valeurs d’enregistrement en fonction des noms de champs et des types de valeurs

  • Types de listes, qui classifient les listes à l’aide d’un type de base d’élément unique

  • Types de fonctions, qui classifient les valeurs de fonction en fonction des types de leurs paramètres et valeurs de retour

  • Types de tables, qui classifient les valeurs de table en fonction des noms de colonnes, des types de colonnes et des clés

  • Types Nullable, qui classifient la valeur Null en plus de toutes les valeurs classifiées par un type de base

  • Types de types, qui classifient les valeurs qui sont des types

L’ensemble de types primitifs comprend les types de valeurs primitives et un certain nombre de types abstraits, qui sont des types qui ne classifient aucune valeur de manière unique : function, table, any, anynonnull et none. Toutes les valeurs de fonction sont conformes au type abstrait function, toutes les valeurs de table au type abstrait table, toutes les valeurs au type abstrait any, toutes les valeurs non-null au type abstrait anynonnull et les valeurs absentes au type abstrait none. Une expression de type none doit déclencher une erreur ou échouer, car aucune valeur ne peut être produite conforme au type none. Notez que les types primitifs function et table sont abstraits, car aucune fonction ou table n’est directement de ces types, respectivement. Les types primitifs record et list ne sont pas abstraits, car ils représentent un enregistrement ouvert sans champs définis et une liste de type any, respectivement.

Tous les types qui ne sont pas membres de l’ensemble fermé de types primitifs plus leurs contreparties non-nullables sont collectivement désignés sous le terme de types personnalisés. Les types personnalisés peuvent être écrits à l’aide d’un type-expression :

type-expression :
      primary-expression

      typeprimary-type
type :
      primary-expression
      primary-type
primary-type :
      primitive-type
      record-type
      list-type
      function-type
      table-type
      nullable-type
primitive-type :
une des valeurs suivantes
      any anynonnull binary date datetime datetimezone duration function list logical
      none null number record table text time type

Les noms primitive-type sont des mots clés contextuels reconnus uniquement dans un contexte de type. L’utilisation de parenthèses dans un contexte de type redéplace la grammaire dans un contexte d’expression régulière, ce qui nécessite l’utilisation du mot clé type pour revenir dans un contexte de type. Par exemple, pour appeler une fonction dans un contexte de type, vous pouvez utiliser des parenthèses :

type nullable ( Type.ForList({type number}) )   
// type nullable {number}

Vous pouvez aussi utiliser des parenthèses pour accéder à une variable dont le nom est en conflit avec un nom primitive-type :

let  record = type [ A = any ]  in  type {(record)} 
// type {[ A = any ]}

L’exemple suivant définit un type qui classifie une liste de nombres :

type { number }

De même, l’exemple suivant définit un type personnalisé qui classifie des enregistrements avec des champs obligatoires nommés X et Y dont les valeurs sont des nombres :

type [ X = number, Y = number ]

Le type attribué d’une valeur est obtenu à l’aide de la fonction de bibliothèque standard Value.Type, comme indiqué dans les exemples suivants :

Value.Type( 2 )                 // type number 
Value.Type( {2} )               // type list 
Value.Type( [ X = 1, Y = 2 ] )  // type record

L’opérateur is est utilisé pour déterminer si le type d’une valeur est compatible avec un type donné, comme indiqué dans les exemples suivants :

1 is number          // true 
1 is text            // false 
{2} is list          // true

L’opérateur as vérifie si la valeur est compatible avec le type donné, et génère une erreur si ce n’est pas le cas. Autrement, il retourne la valeur d’origine.

Value.Type( 1 as number )   // type number 
{2} as text                 // error, type mismatch

Notez que les opérateurs is et as acceptent uniquement des types primitifs nullables comme opérande de droite. M n’offre aucun moyen de vérifier les valeurs en termes de conformité aux types personnalisés.

Un type X est compatible avec un type Y si et seulement si toutes les valeurs conformes à X sont également conformes à Y. Tous les types sont compatibles avec le type any, et aucun type (sauf none lui-même) n’est compatible avec le type none. Le graphique suivant montre la relation de compatibilité. (La compatibilité de type est réflexive et transitive. Elle forme un treillis avec le type any comme valeur supérieure et le type none comme valeur inférieure.) Les noms des types abstraits sont définis en italiques.

Compatibilité des types

Les opérateurs suivants sont définis pour les valeurs de type :

Opérateur Résultat
x = y Égal à
x <> y Différent de
x ?? y Coalesce

Le type natif des valeurs de type est le type intrinsèque type.

Types primitifs

Les types du langage M forment une hiérarchie disjointe enracinée au type any, qui est le type qui classifie toutes les valeurs. Toute valeur M est conforme à un seul sous-type primitif de any. L’ensemble fermé de types primitifs dérivant du type any est le suivant :

  • type null, qui classifie la valeur Null.
  • type logical, qui classifie les valeurs true et false.
  • type number, qui classifie les valeurs numériques.
  • type time, qui classifie les valeurs d’heure.
  • type date, qui classifie les valeurs de date.
  • type datetime, qui classifie les valeurs datetime.
  • type datetimezone, qui classifie les valeurs datetimezone.
  • type duration, qui classifie les valeurs de durée.
  • type text, qui classifie les valeurs de texte.
  • type binary, qui classifie les valeurs binaires.
  • type type, qui classifie les valeurs de type.
  • type list, qui classifie les valeurs de liste.
  • type record, qui classifie les valeurs d’enregistrement.
  • type table, qui classifie les valeurs de table.
  • type function, qui classifie les valeurs de fonction.
  • type anynonnull, qui classifie toutes les valeurs à l’exception de Null.
  • type none, qui classifie aucune valeur.

Type any

Le type any est abstrait, il classifie toutes les valeurs en M, et tous les types en M sont compatibles avec any. Les variables de type any peuvent être liées à toutes les valeurs possibles. any étant abstrait, il ne peut pas être attribué à des valeurs ; autrement dit, aucune valeur n’est directement de type any.

Types de listes

Toute valeur qui est une liste est conforme au type intrinsèque list, qui n’impose aucune restriction sur les éléments dans une valeur de liste.

list-type:
      {item-type}
item-type :
      type

Le résultat de l’évaluation d’un list-type est une valeur de type de liste dont le type de base est list.

Les exemples suivants illustrent la syntaxe de déclaration de types de listes homogènes :

type { number }        // list of numbers type 
     { record }        // list of records type
     {{ text }}        // list of lists of text values

Une valeur est conforme à un type de liste si la valeur est une liste et que chaque élément de cette valeur de liste est conforme au type d’élément du type de liste.

Le type d’élément d’un type de liste indique une limite : tous les éléments d’une liste conforme sont conformes au type d’élément.

Types d’enregistrements

Toute valeur qui est un enregistrement est conforme au type intrinsèque record, qui n’impose aucune restriction sur les valeurs ou les noms des champs dans une valeur d’enregistrement. Une valeur record-type est utilisée pour limiter l’ensemble de noms valides, ainsi que les types de valeurs qui sont autorisés à être associés à ces noms.

record-type:
      [open-record-marker]
      [field-specification-listopt]
      [field-specification-list, open-record-marker]
field-specification-list :
      field-specification
      field-specification,field-specification-list
field-specification :

      optionalopt field-name field-type-specificationopt
field-type-specification :

      =field-type
field-type :
      type
open-record-marker :

      ...

Le résultat de l’évaluation d’un record-type est une valeur de type dont le type de base est record.

Les exemples suivants illustrent la syntaxe de déclaration des types d’enregistrements :

type [ X = number, Y = number] 
type [ Name = text, Age = number ]
type [ Title = text, optional Description = text ] 
type [ Name = text, ... ]

Les types d’enregistrements sont fermés par défaut, ce qui signifie que les champs supplémentaires absents de la fieldspecification-list ne sont pas autorisés à être présents dans les valeurs conformes. L’inclusion du openrecord-marker dans le type d’enregistrement déclare le type comme étant ouvert, ce qui autorise les champs absents de la liste de spécifications de champ. Les deux expressions suivantes sont équivalentes :

type record   // primitive type classifying all records 
type [ ... ]  // custom type classifying all records

Une valeur est conforme à un type d’enregistrement si la valeur est un enregistrement et que chaque spécification de champ dans le type d’enregistrement est satisfaite. Une spécification de champ est satisfaite si l’une des conditions suivantes est remplie :

  • Il existe un nom de champ correspondant à l’identificateur de la spécification dans l’enregistrement, et la valeur associée est conforme au type de la spécification

  • La spécification est marquée comme étant facultative et aucun nom de champ correspondant n’a été trouvé dans l’enregistrement

Une valeur conforme peut contenir des noms de champs qui ne sont pas listés dans la liste de spécifications de champ si et uniquement si le type d’enregistrement est ouvert.

Types de fonctions

Toute valeur de fonction est conforme au type primitif function, qui n’impose aucune restriction sur les types des paramètres formels de la fonction ou sur la valeur de retour de la fonction. Une valeur function-type personnalisée est utilisée pour imposer des restrictions de type sur les signatures des valeurs de fonction conformes.

function-type:
      function (parameter-specification-listopt)function-return-type
parameter-specification-list :
      required-parameter-specification-list
      required-parameter-specification-list
,optional-parameter-specification-list
      optional-parameter-specification-list
required-parameter-specification-list :
      required-parameter-specification
      required-parameter-specification
,required-parameter-specification-list
required-parameter-specification :
      parameter-specification
optional-parameter-specification-list :
      optional-parameter-specification
      optional-parameter-specification
,optional-parameter-specification-list
optional-parameter-specification :

      optionalparameter-specification
parameter-specification :
      parameter-name parameter-type
function-return-type :
      assertion
assertion :

      asnullable-primitive-type

Le résultat de l’évaluation d’un function-type est une valeur de type dont le type de base est function.

Les exemples suivants illustrent la syntaxe de déclaration des types de fonctions :

type function (x as text) as number 
type function (y as number, optional z as text) as any

Une valeur de fonction est conforme à un type de fonction si le type de retour de la valeur de fonction est compatible avec le type de retour du type de fonction et que chaque spécification de paramètre du type de fonction est compatible avec le paramètre formel correspondant à la position de la fonction. Une spécification de paramètre est compatible avec un paramètre formel si le type parameter-type spécifié est compatible avec le type du paramètre formel, et la spécification de paramètre est facultative si le paramètre formel est facultatif.

Les noms des paramètres formels sont ignorés dans le cadre de la détermination de la conformité du type de fonction.

La spécification d’un paramètre comme facultatif fait implicitement en sorte que son type soit nullable. L’action suivante crée des types de fonctions identiques :

type function (optional x as text) as any
type function (optional x as nullable text) as any

Types de tables

Une valeur table-type est utilisée pour définir la structure d’une valeur de table.

table-type:
      tablerow-type
row-type :

      [field-specification-listopt]

Le résultat de l’évaluation d’un table-type est une valeur de type dont le type de base est table.

Le type de ligne d’une table spécifie les noms des colonnes et les types de colonnes de la table en tant que type d’enregistrement fermé. Afin que toutes les valeurs de table soient conformes au type table, son type de ligne est de type record (type d’enregistrement ouvert vide). Ainsi, la table de type est abstraite, puisque aucune valeur de table ne peut avoir le type de ligne du type table (mais toutes les valeurs de table ont un type de ligne qui est compatible avec le type de ligne du type table). L’exemple suivant illustre la construction d’un type de table :

type table [A = text, B = number, C = binary] 
// a table type with three columns named A, B, and C 
// of column types text, number, and binary, respectively

Une valeur table-type contient également la définition des clés d’une valeur de table. Une clé est un ensemble de noms de colonnes. Une clé au plus peut être désignée en tant que clé primaire de la table. (En M, les clés de table n’ont aucune signification sémantique. Toutefois, il est courant de définir des clés sur des tables pour les sources de données externes, comme les bases de données ou les flux OData. Power Query utilise les informations sur les clés pour améliorer les performances des fonctionnalités avancées, comme les opérations de jointure entre sources.)

Vous pouvez utiliser les fonctions de bibliothèque standard Type.TableKeys, Type.AddTableKey et Type.ReplaceTableKeys respectivement pour obtenir les clés d’un type de table, ajouter une clé à un type de table et remplacer toutes les clés d’un type de table.

Type.AddTableKey(tableType, {"A", "B"}, false) 
// add a non-primary key that combines values from columns A and B 
Type.ReplaceTableKeys(tableType, {}) 
// returns type value with all keys removed

Types Nullable

Pour tout type T, une variante pouvant accepter la valeur Null peut être dérivée à l’aide de nullable-type :

nullable-type:
      nullabletype

Le résultat est un type abstrait qui autorise les valeurs de type T ou la valeur null.

42 is nullable number             // true null is
nullable number                   // true

L’attribution de type nullableT se réduit à l’attribution de type null ou typeT. (Rappelez-vous que les types Nullable sont abstraits et qu’aucune valeur ne peut être directement de type abstrait.)

Value.Type(42 as nullable number)       // type number
Value.Type(null as nullable number)     // type null

Vous pouvez utiliser les fonctions de bibliothèque standard Type.IsNullable et Type.NonNullable pour tester la possibilité de valeur Null d’un type et pour supprimer la possibilité de valeur Null d’un type.

Ce qui suit est valable (pour tout type T) :

  • type T est compatible avec type nullable T
  • Type.NonNullable(type T) est compatible avec type T

Les éléments suivants sont équivalents en tant que paires (pour tout type T) :

    type nullable any
    any

    Type.NonNullable(type any)
    type anynonnull

    type nullable none
    type null

    Type.NonNullable(type null)
    type none

    type nullable nullable T
    type nullable T

    Type.NonNullable(Type.NonNullable(type T))
    Type.NonNullable(type T)

    Type.NonNullable(type nullable T)
    Type.NonNullable(type T)

    type nullable (Type.NonNullable(type T))
    type nullable T

Type attribué d’une valeur

Le type attribué d’une valeur est le type auquel une valeur a été déclarée comme étant conforme.

Un type peut être attribué à une valeur à l’aide de la fonction de bibliothèque Value.ReplaceType. Cette fonction retourne une nouvelle valeur avec le type attribué ou génère une erreur si le nouveau type est incompatible avec la valeur.

Quand un type est attribué à une valeur, seule une vérification de conformité limitée a lieu :

  • Le type attribué doit être non abstrait, non nullable et compatible avec le type primitif intrinsèque (natif) de la valeur.
  • Lorsqu’un type personnalisé qui définit la structure est attribué, il doit correspondre à la structure de la valeur.
    • Pour les enregistrements : le type doit être fermé, doit définir le même nombre de champs que la valeur et ne doit contenir aucun champ facultatif. (Les noms de champ et les types de champ du type remplacent ceux actuellement associés à l’enregistrement. Toutefois, les valeurs de champ existantes ne sont pas vérifiées par rapport aux nouveaux types de champ.)
    • Pour les tables : le type doit définir le même nombre de colonnes que la valeur. (Les noms de colonne et les types de colonne du type remplacent ceux actuellement associés à la table. Toutefois, les valeurs de colonne existantes ne sont pas vérifiées par rapport aux nouveaux types de colonne.)
    • Pour les fonctions : le type doit définir le même nombre de paramètres obligatoires, ainsi que le même nombre de paramètres facultatifs, que la valeur. (Les assertions de paramètre et de retour du type, ainsi que ses noms de paramètre, remplacent celles associées au type actuel de la valeur de fonction. Toutefois, les nouvelles assertions n’auront aucun effet sur le comportement réel de la fonction.)
    • Pour les listes : la valeur doit être une liste. (Toutefois, les éléments de liste existants ne sont pas vérifiés par rapport au nouveau type d’élément.)

Les fonctions de bibliothèque peuvent choisir de calculer et d’attribuer des types complexes à des résultats en fonction des types attribués des valeurs d’entrée.

Le type attribué d’une valeur peut être obtenu à l’aide de la fonction de bibliothèque Value.Type. Par exemple :

Value.Type( Value.ReplaceType( {1}, type {number} ) 
// type {number}

Équivalence et compatibilité entre les types

L’équivalence de type n’est pas définie dans M. Une implémentation M peut éventuellement choisir d’utiliser ses propres règles pour effectuer des comparaisons d’égalité entre les valeurs de type. La comparaison de deux valeurs de type pour l’égalité doit déterminer true si elles sont considérées comme identiques par l’implémentation, et false dans le cas contraire. Dans les deux cas, la réponse retournée doit être cohérente si les deux mêmes valeurs sont comparées à plusieurs reprises. Notez que dans une implémentation donnée, la comparaison de certaines valeurs de type identiques (comme (type text) = (type text)) peut retourner true, alors que la comparaison d’autres valeurs (comme (type [a = text]) = (type [a = text])) non.

Vous pouvez déterminer la compatibilité entre un type donné et un type primitif Nullable à l’aide de la fonction de bibliothèque Type.Is, qui accepte une valeur de type arbitraire comme premier argument et une valeur de type primitif Nullable comme deuxième argument :

Type.Is(type text, type nullable text)  // true 
Type.Is(type nullable text, type text)  // false 
Type.Is(type number, type text)         // false 
Type.Is(type [a=any], type record)      // true 
Type.Is(type [a=any], type list)        // false

Il n’existe aucune prise en charge en M pour déterminer la compatibilité d’un type donné avec un type personnalisé.

La bibliothèque standard inclut une collection de fonctions permettant d’extraire les caractéristiques de définition d’un type personnalisé. Des tests de compatibilité spécifiques peuvent par conséquent être implémentés en tant qu’expressions M. Vous trouverez quelques exemples ci-dessous. Pour plus d’informations, consultez la spécification de la bibliothèque M.

Type.ListItem( type {number} ) 
  // type number 
Type.NonNullable( type nullable text ) 
  // type text 
Type.RecordFields( type [A=text, B=time] ) 
  // [ A = [Type = type text, Optional = false], 
  //   B = [Type = type time, Optional = false] ] 
Type.TableRow( type table [X=number, Y=date] ) 
  // type [X = number, Y = date] 
Type.FunctionParameters(
        type function (x as number, optional y as text) as number) 
  // [ x = type number, y = type nullable text ] 
Type.FunctionRequiredParameters(
        type function (x as number, optional y as text) as number) 
  // 1 
Type.FunctionReturn(
        type function (x as number, optional y as text) as number) 
  // type number