Exploring possibilities for an API configuration system and seeking guidance on typing a config object.
Clarification: This pertains to a resources/endpoints configuration for the API, where each resource/endpoint has a defined path containing specific parameters. I'll be using TypeScript terminology for simplicity and clarity.
The challenge lies in having a config object with properties that include nested properties requiring certain substrings.
const pathsSchema = {
foo: {
params: ["id"], // substring would be {id}
},
} as const;
(I create a type from this object. The reason for choosing it to be an object instead of just a type is unclear; feel free to use a type directly.)
An example of the corresponding config object based on this schema:
const config = {
foo: {
path: "foo?id={id}",
},
};
Dealing with a single substring is straightforward:
Path<param extends string> = `${string}{${param}}${string}`
type Paths<schema = typeof pathsSchema> = {
[name in keyof schema]: {
path: Path<schema[name]["param"]>
};
};
However, handling multiple substrings requires generating all possible permutations, which is not efficient.
Update: Please refer to update at end of question.
I currently have a generic type that returns true if a string contains the necessary substrings:
type CheckPath<path extends string, params extends string[]> =
// A. If there is a first param, get it.
params extends [infer param extends string, ...infer rest extends string[]]
? path extends `${string}{${param}}${string}` // B. If path contains param...
? CheckPath<path, rest> // B. ...check next param...
: false // B. ...else, path is invalid.
: true; // A. There are no more params, so path is valid.
Below is the framework. The defineConfig
helper aids in providing typing in a separate config file given by users (e.g., vite.config.ts
for Vite).
const defineConfig = (config: Paths) => config;
const config = defineConfig({
foo: {
path: "foo?id={id}",
},
});
Is there a way to ensure that the object passed to defineConfig
passes the CheckPath
validation? I'm open to alternatives to these types of "validation" types.
Update: Template literal types can now be intersected! Hooray for TypeScript! (Thanks to Alex Wayne for mentioning this.)
Now, my focus is on: how do I convert this schema with a map of names-to-string-arrays into a map of names-to-intersections-of-template-literal-types?