The title might be confusing, so allow me to clarify.
My Objective
I am referring to the code snippet below. I aim to specify the route
property based on the types
property as either a string
or a function that returns a string
.
The Code
Let's begin with a functional example.
// Utilizing TypeScript to define types without actual values provided
const defineArgTypes = <
T extends {
args?: ArgumentsBase | null;
}
>() => null as any as T;
interface ArgumentsBase {
queryParams?: Record<string | number | symbol, any>;
}
type BaseActionMap = Record<
string,
{
types?: { args?: ArgumentsBase };
}
>;
type ActionMapItem<Item extends BaseActionMap[string]> = {
types?: Item['types'];
};
type ActionMap<BaseConfig extends BaseActionMap> = {
[Property in keyof BaseConfig]: ActionMapItem<BaseConfig[Property]>;
};
type ConfigMapItem<Item extends BaseActionMap> = {
route: Item['types'] extends { args: ArgumentsBase }
? (args: Item['types']['args']['queryParams']) => string
: string;
};
type ConfigMap<AMap extends ActionMap<any>> = {
[Property in keyof AMap]: ConfigMapItem<AMap[Property]>;
};
const defineActions = <Data extends BaseActionMap>(
actions: Data,
config: ConfigMap<Data>
) => {
// irrelevant code nested here
};
const config = defineActions(
{
getTodos: {},
getTodo: {
types: defineArgTypes<{ args: { queryParams: { id: string } } }>(),
},
},
{
getTodo: {
route: (d) => `todos/${d.id}`,
},
getTodos: {
route: 'todos',
},
}
);
In the above code, it is necessary to define "actions (getTodos, getTodo)" twice.
Is there a way to streamline this to the following while maintaining conditional types for the route
properties?
const config = defineActions(
{
getTodos: {
route: 'todos',
},
getTodo: {
types: defineArgTypes<{ args: { queryParams: { id: string } } }>(),
route: (d) => `todos/${d.id}`,
},
}
);