I have type that looks like this:
export type RoutePermissions =
| 'page:mypage1/subroute1'
| 'page:mypage1/subroute2'
| 'page:mypage1/subroute3'
| 'page:mypage2/subroute1'
| 'page:mypage3/subroute1'
I want to add a type that matches the type partially, for example:
type PartialRouteMatchType = ???;
const partialRouteMatch = (partialRoute: PartialRouteMatchType) => {};
partialRouteMatch('page:mypage1'); // ok
partialRouteMatch('page:mypage2'); // ok
partialRouteMatch('page:mypage22'); // should fail
Is this possible in Typescript? Thank you!
EDIT: Apologies, the first example wasn't good. Added more detail.
CodePudding user response:
It is, with template literal types:
type PartialRouteMatchType = RoutePermissions extends `${infer S}/${string}` ? S : never;
Here we have just defined a type that infers all the strings that start each string in the type RoutePermissions
. Pretty simple and efficient and requires no changes to anything else. Also, it works even if the routes contain more subroutes i.e. "page:mypage1/one/two/three"
still results in "page:mypage1"
.
CodePudding user response:
If you are looking for a function which only accepts sub-strings of a union of string literal types, then maybe this will help you.
const partialRouteMatch = <T extends string>(
partialRoute: RoutePermissions extends infer U
? U extends `${string}${T}${string}`
? T
: never
: never
) => {};
partialRouteMatch('page:mypage1'); // ok
partialRouteMatch('page:mypage2'); // ok
partialRouteMatch('page2/subroute'); // ok
partialRouteMatch('page:mypage22'); // fails
or maybe you want the substring to only start at the beginning of the string.
const partialRouteMatch = <T extends string>(
partialRoute: RoutePermissions extends infer U
? U extends `${T}${string}`
? T
: never
: never
) => {};
partialRouteMatch('page:mypage1'); // ok
partialRouteMatch('page:mypage2'); // ok
partialRouteMatch('page2/subroute'); // fails
partialRouteMatch('page:mypage22'); // fails
CodePudding user response:
You want a string template literal type:
type PartialRouteMatchType = `page:mypage1${string}`;
const partialRouteMatch = (partialRoute: PartialRouteMatchType) => {};
partialRouteMatch('page:mypage1'); // fine
partialRouteMatch('bad'); // error
CodePudding user response:
In the general case this will work for any route-"like"
type RouteLike = `page:mypage${number}${ '' | `/subroute${number}` }`;
const route = (x :RouteLike) => {};
//correct
route('page:mypage1');
route('page:mypage22');
route('page:mypage1/subroute3');
//errors
route('page');
route('page:mypage1/');
route('page:mypage1/subroute');
route('page:mypage1x');
route('page:mypage1/subroutex');
But if you really want it to only accept an exhaustive list, you'll need to do something like this:
type RouteLike<
T extends number,
U extends number
> = `page:mypage${T}${ '' | `/subroute${U}` }`;
type ValidRoute = (
RouteLike<1, 1|2|3> |
RouteLike<2, 1 > |
RouteLike<3, 1 >
);
const route = (x :ValidRoute) => {};
//correct
route('page:mypage1');
route('page:mypage12'); //this now errors
route('page:mypage1/subroute3');
route('page:mypage3/subroute1');
//errors
route('page');
route('page:mypage1/');
route('page:mypage1/subroute');
route('page:mypage1x');
route('page:mypage1/subroutex');