Projeção de campos
fields restringe o tipo de retorno, não só a resposta. Peça dois campos e a linha devolvida tem esses dois
mais o id. Ler qualquer outro campo é um erro de compilação, não um undefined em tempo de execução:
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); // tipado: só existem id/name nesta linha
// console.log(state.color); // erro de compilação: `color` não foi pedido
}
Um array literal inline não precisa de as const. O mesmo vale para iterate e retrieve e, por ser a mesma
afirmação, para as escritas: create, update e upsert aceitam fields e restringem a resposta da mesma
forma.
const created = await client.v2.workspaces.projects.states.create(
"acme",
"ENG",
{ name: "In Review", color: "#4ECDC4" },
{ fields: ["id", "name"] }
);
console.log(created.name); // restrito; `created.color` não compilaria
Listas de campos construídas em tempo de execução
Uma lista de campos construída em tempo de execução (não um literal) continua a precisar de ser tipada como
nomes de campo. Um string[] simples não é atribuível a readonly StateField[] e falha com um longo erro
de incompatibilidade de overload. A linha é restringida ao tipo do elemento da lista, por isso tipe a
variável de forma tão restrita quanto aquela que efetivamente utiliza:
import { PlaneClient, v2 } from "@hoyasumii/plane";
const client = new PlaneClient({ baseUrl: "https://api.plane.so", apiKey: "..." });
// Restringido a estes dois nomes, mesmo que o valor seja escolhido em tempo de execução.
const wanted: ("id" | "name")[] = includeColor ? ["id", "name"] : ["id"];
const page = await client.v2.workspaces.projects.states.list("acme", "ENG", { fields: wanted });
// Tipado como a união inteira, isto restringe a "todos os campos", ou seja, a linha completa.
const anything: v2.StateField[] = includeColor ? ["id", "name", "color"] : ["id", "name"];
const unnarrowed = await client.v2.workspaces.projects.states.list("acme", "ENG", { fields: anything });
"all" é um valor de campo válido que significa "todos os campos", e produz corretamente o tipo completo da
linha.
Respostas esparsas significam que todo campo de leitura, exceto id, é opcional nos modelos. Verifique se há
undefined em vez de presumir a presença. Quando a lista foi construída dinamicamente, row.$loaded.present
indica o que efetivamente voltou.
Chamadas navegadas não restringem
Esta limitação é a razão pela qual a forma plana se mantém pública. Uma chamada navegada resolve-se contra a
assinatura geral, por isso aceita fields, mas não restringe:
const eng = await client.v2.workspaces.projects.retrieve("acme", "ENG");
const listed = await eng.states.list({ fields: ["id", "name"] }); // Page<State>, sem restrição
O TypeScript elimina um parâmetro de tipo quando infere através do tipo condicional por detrás de uma
propriedade de navegação, pelo que o overload que restringe não pode ser transportado. Reproduzir o conjunto de
overloads seria pior, não melhor: a inferência eliminaria F até à sua restrição e afirmaria que todos os
campos estão presentes. Quando quiser a linha restrita, chame o recurso pelo caminho plano:
client.v2.workspaces.projects.states.list("acme", "ENG", { fields: [...] }).
expand e order_by
expand embute objetos relacionados ({ expand: ["state", "labels"] } num work item), validado contra os
valores permitidos da operação em v2.EXPAND.
order_by é validado da mesma forma que fields, contra uma união gerada (StateOrderBy, LabelOrderBy, …).
Um literal fora dessa união é um erro de compilação, e um valor construído em tempo de execução é rejeitado do
lado do cliente por encodeOrderBy, em vez de chegar ao servidor como um 400.
await client.v2.workspaces.projects.states.list("acme", "ENG", { order_by: "-created_at" });