Reputation: 549
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.
Upvotes: 2
Views: 1443
Reputation: 13013
Going by the accepted answer it looks like Grandas meant they wanted to match the root of the route (a string partial match to the first slash only or end-of-string), not a partial match of the route (a string partial match to any slash or end-of-string).
I'm going to make my answer about the latter in case anyone needs it, because unfortunately caTS' elegant inference wont work for this; as template literal inference only extracts up to the first slash, not exhaustively searches to infer all possible splits.
You would just need to more-descriptively list your "RoutePermissions", which will allow us to utilise template literal types without infer (basically building up the union of all possible types, as opposed to trying to crack apart a simplified union into an exhaustive list which isn't possible to an unknown depth of path).
type Route<
T extends string,
U extends string|never = never
> = `${T}${ '' | `/${U}` }`;
type ValidRoute = (
Route<'page:mypage0'> |
Route<'page:mypage1',
'subroute1'|'subroute2'|'subroute3'
> |
Route<'page:mypage2',
Route<'subroute1',
'subroute2'
>
> |
Route<'page:mypage3',
'subroute1'
>
);
const route = (x :ValidRoute) => {};
//correct
route('page:mypage0');
route('page:mypage1');
route('page:mypage1/subroute3');
route('page:mypage2/subroute1');
route('page:mypage2/subroute1/subroute2');
route('page:mypage3/subroute1');
//errors
route('page:mypage2/subroute2');
route('page:mypage12');
route('page');
route('page:mypage1/');
route('page:mypage1/subroute');
route('page:mypage1/subroute4');
Upvotes: 0
Reputation: 26324
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"
.
Upvotes: 2
Reputation: 23825
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
Upvotes: 1
Reputation: 187024
You want a string template literal type:
type PartialRouteMatchType = `page:mypage1${string}`;
const partialRouteMatch = (partialRoute: PartialRouteMatchType) => {};
partialRouteMatch('page:mypage1'); // fine
partialRouteMatch('bad'); // error
Upvotes: 0