A utility type for prefixing a type's properties with a string:
export type WithPrefix<Prefix extends string, T, Separator extends string = '/'> = {
[K in keyof T as `${Prefix}${Separator}${string & K}`]: T[K];
};
Simply provide a prefix, type and optional separator (defaults to /).
type UserApi = {
get: GetFn<User>;
set: SetFn<User>;
all: ListFn<User>;
};
const api: WithPrefix<'/api', UserApi> = {
'/api/get': getUser,
'/api/set': setUser,
'/api/all': listUsers,
};
Example: API routes
When handling interactions between a front-end and back-end we might have an API that looks like this:
type CartApi = {
checkout: CheckoutFn;
view: GetFn<Cart>;
add: PostFn<CartItem | CartItem[]>;
remove: DeleteFn<CartItem | CartItem[]>;
update: PatchFn<CartItem | CartItem[]>;
};
To ensure that every API method has a corresponding server route we can use the WithPrefix utility type:
const cartApi: WithPrefix<'/cart', CartApi> = {
'/cart/checkout': checkout,
'/cart/view': viewCart,
'/cart/add': addToCart,
'/cart/remove': removeFromCart,
'/cart/update': updateCart,
};
for (const [route, handler] of Object.entries(cartApi)) {
app[handler.method](route, handler);
}
Whenever we define additional methods on our CartApi
:
type CartApi = {
checkout: CheckoutFn;
view: GetFn<Cart>;
add: PostFn<CartItem | CartItem[]>;
remove: DeleteFn<CartItem | CartItem[]>;
update: PatchFn<CartItem | CartItem[]>;
empty: DeleteFn<Cart>;
};
TypeScript will show an error:
// TS2741: Property '/cart/empty' is missing in type WithPrefix<'/cart', CartApi>
const cartApi: WithPrefix<'/cart', CartApi> = {
'/cart/checkout': checkout,
'/cart/view': viewCart,
'/cart/add': addToCart,
'/cart/remove': removeFromCart,
'/cart/update': updateCart,
};
Breaking it down
This utility uses mapped types, template literal types and key remapping:
export type WithPrefix<Prefix extends string, T, Separator extends string = '/'> = {
[K in keyof T as `${Prefix}${Separator}${string & K}`]: T[K];
};
- We define a type with:
- A
Prefix
to add to each property. - A type
T
to prefix. - A
Separator
to separate the prefix from the property name.
- A
- We iterate over each property of
T
and interpolate the prefix and separator with the property name${Prefix}${Separator}${string & K}
.
This gives us a resulting type with our prefixed property names.
Hopefully you find this useful :)
Top comments (0)