Aller au contenu principal

Projection de champs

fields restreint le type de retour, pas seulement la réponse. Demandez deux champs, et la ligne que vous obtenez a ces deux champs plus id. Lire quoi que ce soit d'autre est une erreur de compilation, pas un undefined à l'exécution :

const page = await client.v2.workspaces.projects.states.list("acme", "ENG", { fields: ["id", "name"] });
for (const state of page.data) {
console.log(state.id, state.name); // typé : seuls id/name existent sur cette ligne
// console.log(state.color); // erreur de compilation : `color` n'a pas été demandé
}

Un littéral de tableau en ligne n'a besoin d'aucun as const. Cela vaut aussi pour iterate et retrieve, et, puisque c'est la même affirmation, pour les écritures : create, update et upsert acceptent fields et restreignent leur réponse de la même façon.

const created = await client.v2.workspaces.projects.states.create(
"acme",
"ENG",
{ name: "In Review", color: "#4ECDC4" },
{ fields: ["id", "name"] }
);
console.log(created.name); // restreint ; `created.color` ne compilerait pas

Listes de champs construites à l'exécution​

Une liste de champs construite à l'exécution (pas un littéral) doit tout de même être typée comme des noms de champ. Un simple string[] n'est pas assignable à readonly StateField[] et échoue avec une longue erreur d'incompatibilité de surcharge. La ligne est restreinte au type d'élément de la liste, donc typez la variable aussi précisément que ce que vous utilisez réellement :

import { PlaneClient, v2 } from "@hoyasumii/plane";

const client = new PlaneClient({ baseUrl: "https://api.plane.so", apiKey: "..." });

// Restreint à ces deux noms, même si la valeur est choisie à l'exécution.
const wanted: ("id" | "name")[] = includeColor ? ["id", "name"] : ["id"];
const page = await client.v2.workspaces.projects.states.list("acme", "ENG", { fields: wanted });

// Typé comme l'union entière à la place, ceci restreint à « tous les champs », c.-à-d. la ligne complète.
const anything: v2.StateField[] = includeColor ? ["id", "name", "color"] : ["id", "name"];
const unnarrowed = await client.v2.workspaces.projects.states.list("acme", "ENG", { fields: anything });

"all" est une valeur de champ légale signifiant « tous les champs », et elle rend bien le type de ligne complet.

Les réponses partielles signifient que chaque champ de lecture, sauf id, est facultatif dans les modèles. Vérifiez undefined plutôt que de présumer sa présence. Quand la liste a été construite dynamiquement, row.$loaded.present vous dit ce qui est réellement revenu.

Les appels navigués ne restreignent pas​

Cette limitation est la raison pour laquelle la forme plate reste publique. Un appel navigué se résout contre la signature générale, donc il accepte fields mais ne restreint pas :

const eng = await client.v2.workspaces.projects.retrieve("acme", "ENG");
const listed = await eng.states.list({ fields: ["id", "name"] }); // Page<State>, non restreint

TypeScript efface un paramètre de type quand il l'infère à travers le type conditionnel derrière une propriété de navigation, donc la surcharge de restriction ne peut pas être reportée. Faire correspondre l'ensemble de surcharges à la place serait pire, pas mieux : l'inférence effacerait F à sa contrainte et prétendrait que tous les champs sont présents. Quand vous voulez la ligne restreinte, appelez la ressource de façon plate : client.v2.workspaces.projects.states.list("acme", "ENG", { fields: [...] }).

expand et order_by​

expand inclut des objets liés ({ expand: ["state", "labels"] } sur un élément de travail), validé contre les valeurs autorisées de l'opération dans v2.EXPAND.

order_by est validé de la même façon que fields, contre une union générée (StateOrderBy, LabelOrderBy, …). Un littéral hors de cette union est une erreur de compilation, et une valeur construite à l'exécution est rejetée côté client par encodeOrderBy plutôt que d'atteindre le serveur comme un 400.

await client.v2.workspaces.projects.states.list("acme", "ENG", { order_by: "-created_at" });