You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TypeScript Version: Testing in both 2.9.2 && 3.0.0-dev.201xxxxx
Search Terms:
generics, jsx, conditional types
Code
I am trying to write a typesafe wrapper around React-Router's Link component. I would like to make the component generic to my routes map, while providing a clean API for consumers of the component.
If the route has no parameters, I want the component to only require the routeName prop, while routes with parameters require both the routeName and params associated with that route object passed in as props.
import*asReactfrom'react';import{RouteasIRoute,RouteParams,param}from'../interfaces/types';import{LinkasReactRouterLink}from'react-router-dom';import{route}from'../route';typeLinkWithNoParams<TextendsRecord<string,IRoute<any>>,KextendskeyofT>={routeName: K;};typeLinkWithParams<TextendsRecord<string,IRoute<any>>,KextendskeyofT>={routeName: K;// extracts the "param" values from a route tupleparams: RouteParams<T[K]>;};exporttypeLinkProps<TextendsRecord<string,IRoute<any>>,KextendskeyofT>=keyofRouteParams<T[K]>extendsnever ? LinkWithNoParams<T,K> : LinkWithParams<T,K>;functionhasParams(e: any): e is LinkWithParams<any,any>{returne&&e.params;}exportconstcreateLink=<TextendsRecord<string,IRoute<any>>>(routes: T)=>{functionLink<KextendskeyofT>(props: LinkProps<T,K>){if(hasParams(props)){return<ReactRouterLinkto={routes[props.routeName].create(props.params)}/>;}else{return<ReactRouterLinkto={routes[props.routeName].create({})}/>;}}returnLink;};// Used as our discriminantenumRouteNames{VIEW='VIEW',VIEW_DETAILS='VIEW_DETAILS',}constRoutes={[RouteNames.VIEW]: route(['view']),[RouteNames.VIEW_DETAILS]: route(['view',param('id'),param('otherId')]),};constLink=createLink(Routes);// Conditional Type Sanity ChecktypeViewRouteParams=keyofRouteParams<typeofRoutes[RouteNames.VIEW]>;// type is nevertypeViewDetailsRouteParams=keyofRouteParams<typeofRoutes[RouteNames.VIEW_DETAILS]>;// type is "id" | "otherId"typeViewLinkProps=LinkProps<typeofRoutes,RouteNames.VIEW>;// type is { routeName: RouteNames.VIEW }typeViewDetailsLinkProps=LinkProps<typeofRoutes,RouteNames.VIEW_DETAILS>;// type is { routeName: RouteNames.VIEW_DETAILS, params: { id: string, otherId: string }}// "correct" behaviorconstcorrect=<LinkrouteName={RouteNames.VIEW}/>// no errorconstcorrect2=<LinkrouteName={RouteNames.VIEW}params={{}}/>// params does not exist on type ...constcorrect3=<Link<RouteNames.VIEW_DETAILS>routeName={RouteNames.VIEW_DETAILS}/>;// error of "property params is missing in type.."constcorrect4=(<Link<RouteNames.VIEW_DETAILS>routeName={RouteNames.VIEW_DETAILS}params={{}}/>);// error of "property id is missing in type..."// "incorrect" behaviorconstSHOULD_FAIL_BUT_DOESNT=<LinkrouteName={RouteNames.VIEW_DETAILS}/>;//expectfailure"property params is missing in type..."constSHOULD_NOT_FAIL_BUT_DOES=<LinkrouteName={RouteNames.VIEW_DETAILS}params={{}}/>;// property params doesnt not exist on type `IntrinsicAttributes & LinkWithNoParams
Expected behavior:
Link does not require explicit generic argument--can infer based off the routeName prop passed to the JSX component.
Actual behavior:
Link required explicitly passing in generic argument.
@mhegazy Sorry, that's quite an oversight by me. I am trying to work on a simpler repro right now, but for now here are the two additional files: ../interfaces/types.ts ../route.ts
There's some pretty crazy type-level stuff going on with the UnionToIntersection type, and what I'm trying to do is inherently pretty complicated--so I'm not entirely surprised at the inference engine not being super psyched. I will try to get a more minimal reproduction to simplify this.
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
Uh oh!
There was an error while loading. Please reload this page.
TypeScript Version: Testing in both 2.9.2 && 3.0.0-dev.201xxxxx
Search Terms:
generics, jsx, conditional types
Code
I am trying to write a typesafe wrapper around React-Router's Link component. I would like to make the component generic to my routes map, while providing a clean API for consumers of the component.
If the route has no parameters, I want the component to only require the
routeName
prop, while routes with parameters require both therouteName
andparams
associated with that route object passed in as props.Expected behavior:
Link does not require explicit generic argument--can infer based off the routeName prop passed to the JSX component.
Actual behavior:
Link required explicitly passing in generic argument.
Related Issues:
Possibly #23412
The text was updated successfully, but these errors were encountered: