Reputation: 1379
Following up on my question Force object properties to pick one interface key, I have now a problem when using the return value of one function as an argument to another function - though the types should be the same within the same key.
Consider this code:
interface ItemTypePerModule {
module1: { foo1: string }
module2: { foo2: string }
}
type ModuleMapping<T> = {
[P in keyof T]: {
read: () => Promise<T[P]>
update: (existingItem: T[P]) => Promise<void>
}
}
const moduleReadAndUpdate: ModuleMapping<ItemTypePerModule> = {
module1: {
read: () => { return Promise.resolve( {foo1: "myItem"} ) },
update: (existingItem) => {
console.log(existingItem.foo1)
return Promise.resolve()
},
},
module2: {
read: () => { return Promise.resolve( {foo2: "myItem"} ) },
update: (existingItem) => {
console.log(existingItem.foo2)
return Promise.resolve()
},
}
}
async function doStuffInAllModules() {
const modules = Object.keys(moduleReadAndUpdate) as Array<keyof ItemTypePerModule>
for(const mod of modules) {
const item = await moduleReadAndUpdate[mod].read()
await moduleReadAndUpdate[mod].update(item)
}
}
In the last code line (await moduleReadAndUpdate[mod].update(item)
) I get the error
Argument of type '{ foo1: string; } | { foo2: string; }' is not assignable to parameter of type '{ foo1: string; } & { foo2: string; }'.
Type '{ foo1: string; }' is not assignable to type '{ foo1: string; } & { foo2: string; }'.
Property 'foo2' is missing in type '{ foo1: string; }' but required in type '{ foo2: string; }'.
Actually, within the same module the return value of read
and the param for update
should be typed the same. I.e. when I'm processing module1
then the type is always { foo1: string }
and never { foo2: string }
. However, according to the error message it seems TypeScript doesn't really "understand" and does some weird assumptions with unions and intersections instead.
So, I'm inclined to think that this might be a TypeScript limitation. But how can I work around it?
Upvotes: 0
Views: 202
Reputation: 250206
You can't correlate mod
and item
in this way, typescript just doesn't understand this pattern. You will need to use a type assertion:
async function doStuffInAllModules() {
const modules = Object.keys(moduleReadAndUpdate) as Array<keyof ItemTypePerModule>
for(const mod of modules) {
const item = await moduleReadAndUpdate[mod].read()
await moduleReadAndUpdate[mod].update(item as never)
}
}
Upvotes: 1