Reputation: 28810
I have the following code:
export async function loadInitialProps(routes: AsyncRouteProps[], pathname: string, ctx: any): Promise<InitialProps> {
const promises: Promise<any>[] = [];
const match = routes.find((route: AsyncRouteProps) => {
const matched = matchPath(pathname, route);
if (matched && route.component && isAsyncComponent(route.component)) {
promises.push(
route.component.load
? route.component.load().then(() => route.component.getInitialProps({ matched, ...ctx }))
: route.component.getInitialProps({ matched, ...ctx })
);
}
return !!matched;
});
return {
match,
data: (await Promise.all(promises))[0]
};
}
I thought I could narrow the union type with my isAsyncComponent
guarded function:
export function isAsyncComponent(Component: AsyncRouteableComponent): Component is AsyncRouteComponentType<any> {
return (<AsyncRouteComponentType<any>>Component).load !== undefined;
}
My AsyncRouteableComponent
type is a union type:
export type AsyncRouteableComponent<Props = any> =
| AsyncRouteComponentType<RouteComponentProps<Props>>
| React.ComponentType<RouteComponentProps<Props>>
| React.ComponentType<Props>;
But typescript thinks the code in the resolved promise function route.component.load().then(() => route.component.getInitialProps({ matched, ...ctx }))
is not the type I expect.
if (matched && route.component && isAsyncComponent(route.component)) {
promises.push(
route.component.load
? route.component.load().then(() => route.component.getInitialProps({ matched, ...ctx }))
: route.component.getInitialProps({ matched, ...ctx })
);
}
I get the error message:
Property 'getInitialProps' does not exist on type 'AsyncRouteableComponent<any>'. Property 'getInitialProps' does not exist on type 'ComponentClass<RouteComponentProps<any, StaticContext>>'.
So it does not appear to have narrowed it down after my guard clause.
I can fix it by doing this but I thought the guard function would mean I did not need to do this:
if (matched && route.component && isAsyncComponent(route.component)) {
const component = route.component as AsyncRouteComponentType<any>;
promises.push(
component.load
? component.load().then(() => component.getInitialProps({ matched, ...ctx }))
: component.getInitialProps({ matched, ...ctx })
);
}
Upvotes: 1
Views: 636
Reputation: 30919
Narrowing for a mutable local variable such as route
does not apply inside callbacks such as your then
callback because TypeScript doesn't trust that the local variable won't be reassigned before the callback executes. See this thread for a little more information. The workaround is to copy route
(or route.component
) to a const
variable before you call the guard function.
Upvotes: 1