No CMS de carga útil, muitos campos de relacionamento polimórfico têm um tipo como o seguinte:
interface TypeA { a: 1 }
interface TypeB { b: 2 }
interface TypeC { c: 3 }
type Example = {
items:
| (
| {
relationTo: 'type-a';
value: number | TypeA;
}
| {
relationTo: 'type-b';
value: number | TypeB;
}
| {
relationTo: 'type-c';
value: number | TypeC;
}
/* ... any number of these types ... */
)[]
}
Os números podem ser retornados da API se a permissão para ler o valor for negada ou se a solicitação atingir a profundidade máxima permitida.
Estou com dificuldades para escrever um guarda de tipos reutilizável/genérico que filtre os number
campos value
. Idealmente, gostaria de uma função que, dado qualquer campo de relacionamento polimórfico, retorne o mesmo array, mas com o número de itens filtrados e os tipos atualizados (ou seja, valor, mas sem number
).
Uma solução como essa não funciona, porque o TypeScript espera que todos os TypeA
, TypeB
, TypeC
, etc sejam do mesmo tipo genérico T
.
function numberGuard<T>(
item: {
relationTo: string,
value: number | T
}
): item is {
relationTo: string,
value: T
} {
return typeof item.value !== "number"
}
function polyRelationshipMany<T>(
items: {
relationTo: string,
value: number | T
}[]
) {
return items.filter(numberGuard);
}
declare const e: Example
const z = polyRelationshipMany(e.items)[0] // error!
// Note the error:
/*
Argument of type '({ relationTo: "type-a"; value: number | TypeA; } | { relationTo: "type-b"; value: number | TypeB; } | { relationTo: "type-c"; value: number | TypeC; })[]' is not assignable to parameter of type '{ relationTo: string; value: number | TypeA; }[]'.
Type '{ relationTo: "type-a"; value: number | TypeA; } | { relationTo: "type-b"; value: number | TypeB; } | { relationTo: "type-c"; value: number | TypeC; }' is not assignable to type '{ relationTo: string; value: number | TypeA; }'.
Type '{ relationTo: "type-b"; value: number | TypeB; }' is not assignable to type '{ relationTo: string; value: number | TypeA; }'.
Types of property 'value' are incompatible.
Type 'number | TypeB' is not assignable to type 'number | TypeA'.
Type 'TypeB' is not assignable to type 'number | TypeA'.
*/