Reputation: 997
EDIT:
I'm playing with typing because I would like to avoid returning Primise<any[]>
, what I'm trying to achieve is to get the correct typing for every position in the returned list based on the list passed as a parameter.
EDIT 2:
The error that I was getting has been solved by @outoftouch, but unfortunately, the function typing return is like this:
const enabledResources: ({
firstData: 'Some data',
secondData: 'Some other data'
} | {
thirdData: 'Some data',
fourthData: 'Some other data'
} | undefined)[]
The desired result is instead:
const enabledResources: [
{ firstData: 'Some data', secondData: 'Some other data' } | undefined,
{ thirdData: 'Some data', fourthData: 'Some other data' } | undefined
]
As you can see every list position (in this case position 0 and 1) already have their typing inferred by the Resource list passed as param.
I would like to have a function that gets as the first parameter a list of Resource
that has a field named getResource
that is a function that returns Promise<T>
.
The function has to verify if the user is authorized to get that data and, if it is, return that data in the same position that was passed as a parameter, or otherwise returns undefined
in that list place.
An example of how I would like to use this function is:
const enabledResources = await getPageResources([
{
scope: 'some scope', // in User scopes
getResource: Promise.resolve({ firstData: 'Some data', secondData: 'Some other data' }),
},
{
scope: 'some other scope', // Not in user scopes
getResource: Promise.resolve({ thirdData: 'Some data', fourthData: 'Some other data' }),
},
]);
// This should log { firstData: 'Some data', secondData: 'Some other data' }
console.log(enabledResources[0])
// This should log undefined
console.log(enabledResources[1])
I've tried to achieve that result by writing this function but I got errors in typing:
export type Resource = {
scope: Scope
getResource: () => Promise<unknown>
}
export type Role = {
name: string
scopes: Scope[]
}
export async function getPageResources<T extends readonly Resource[]>(
wantedResources: T,
context: GetServerSidePropsContext<ParsedUrlQuery, PreviewData>
// I get error in the next line that says Type '"getResource"' cannot be used to index type 'T[P]'
): Promise<{ -readonly [P in keyof T]: Awaited<ReturnType<T[P]['getResource']>> | undefined }> {
const session = await getSession(context)
const userRole = session?.role as Role | null
const resourcesToWait = wantedResources.map((e) => {
const isInUserScopes = userRole?.scopes.includes(e.scope)
return isInUserScopes ? e.getResource() : undefined
})
const resources = await Promise.all(resourcesToWait)
return resources;
}
I actually don't get why this is happening, in my mind T[P]
should be the element in the list passed as a parameter and I should get the awaited returned type (or undefined), typed in the correct list position.
What am I doing wrong?
Upvotes: 0
Views: 98
Reputation:
You've might of forgotten that P
could be any key of the array. For example, if P
was "indexOf"
, T[P]
would be a function, and functions do not have a getResponse
method.
You have to explicitly check if T[P]
is a Resource first with extends
:
): Promise<{
-readonly [P in keyof T]: T[P] extends Resource
? Awaited<ReturnType<T[P]['getResource']>> | undefined
: T[P];
}> {
Upvotes: 1