Typescript: Cómo aplicar la misma función a diferentes tipos de arreglos
Esta función de TypeScript debería ser aplicable a dos tipos diferentes de arreglos (es decir, arreglos con diferentes interfaces) que ambos tienen dos propiedades (id y count) en común. La función actualiza la propiedad count de un ítem O tarea con un id dado. Pero obtengo un error de TypeScript en el comando "find": "La expresión no es invocable." Si elimino una de las Interfaces, es decir, si solo defino el parámetro de entrada "items" como un arreglo de Item (Item[]) O un arreglo de Task (Task[]), funciona. Pero quiero aplicar la función a ambos tipos de arreglos.
//interface definitions
export interface Item {
id: number,
count: number,
other: number
}
export interface Task {
id: number,
count: number,
more: string
}
//function
function UpdateCount(items: Item[] | Task[], id: number, count: number) {
let item = items.find(obj => obj.id === id);
if (item !== undefined) {
item.count = count;
}
}
Respuesta #1
Los detalles en el mensaje de error son útiles:
Cada miembro del tipo de unión... tiene firmas, pero ninguna de esas firmas es compatible entre sí.
Lo que está diciendo es que esta firma de función:
find((obj: Item, index: number, array: Item[]): Item | undefined
y esta firma de función:
find((obj: Task, index: number, array: Task[]): Task | undefined
son incompatibles.
En un comentario, captain-yossarian señaló un par de cuestiones relevantes en la lista de problemas de TypeScript:
- Array.reduce no funciona
- Los métodos .map() .reduce() .filter() fallan con el error "Esta expresión no es llamable." en un arreglo válido con tipo unión
Puedes resolverlo de un par de maneras. Probablemente la más simple es definir una interfaz que Item y Task puedan extender ambos:
//interface definitions
interface Base {
id: number,
count: number,
}
export interface Item extends Base {
other: number
}
export interface Task extends Base {
more: string
}
Luego, la función puede simplemente aceptar Base, o usar un parámetro genérico que extienda Base:
function UpdateCount<ElementType extends Base>(items: ElementType[], id: number, count: number) {
let item = items.find(obj => obj.id === id);
if (item !== undefined) {
item.count = count;
}
}
No tienes que definir la interfaz por separado, podrías simplemente usar {id: number, count: number} en lugar de Base en esa función, pero entonces si cambias ese aspecto común de Item o Task, tu función ya no coincidirá con ellos.
Enlace del parque
Respuesta #2
Mi solución sería:
//interface definitions
interface BaseItem {
id: number,
count: number
}
export interface Item extends BaseItem {
other: number
}
export interface Task extends BaseItem {
more: string
}
//function
function UpdateCount<T extends BaseItem>(items: T[], id: number, count: number) {
const item = items.find(i => i.id === id);
if (!item) return;
item.count = count;
}
