I've been trying for a while to create a custom ( typed with typescript ) URLBuilder (with builder design pattern) functionality, which I want to design to be as reusable as possible and right now I'm stuck with the first part of the URL, which is the path.
The query part was a bit easier and I managed to do it, but the path part is harder than expected for me.
1. The first way is by passing the endpoints directly to the constructor:
type Path = {
path1: "fixtures" | "teams";
path2: { fixtures: "statistics" | "lineups"; teams: "seasons" | "countries" };
};
class URLMultiplePaths<P extends Path> {
constructor(path1: P["path1"], path2: P["path2"][P["path1"]]) {}
setPath(path: P) {
return this;
}
}
const url = new URLMultiplePaths("fixtures", "");
I want every next path to be typed depending on the previous one, and when press ctr + space
inside the quotes when passing arguments, to get only the options that are typed depending on the path before.
So if I set the path1 to be fixtures
the options for the next path should be statistics
or lineups
and if I set the path1 to be teams
the options for the path2 to be seasons
or countries
And this - path2: P["path2"][P["path1"]]
in theory should work, but typescript can not infer the type from that lie of code.
Keep in mind that the goal is for this builder to work with different number of params...
2. The second way is by passing the endpoints to the setPath
method:
this way I can chain the paths one by one. But I can't figure out how every next path to be typed depending on the previous one. So that is why I'm trying to implement the example above and then I can think of implementing it with the method and chaining of paths.
But if you have an idea about the second one please do share I think it would be a bit better and maybe cleaner.
I think you can do this with conditional types:
type FixturePath = "fixtures";
type TeamsPath = "teams";
type OtherPath = "other";
type GenericPath<T extends FixturePath | TeamsPath | OtherPath> = {
path1: T;
path2: T extends FixturePath ? "statistics" | "lineups" :
T extends TeamsPath ? "seasons" | "countries" :
T extends OtherPath ? "foo" | "bar" : any;
};
// if you don't want the generic type parameter:
type Path = GenericPath<FixturePath> | GenericPath<TeamsPath> | GenericPath<OtherPath>;
const url1: GenericPath<TeamsPath> = {path1: "teams", path2: "seasons"}
const url2: Path = {path1: "teams", path2: "seasons"}