Build a typesafe ApiRoute type using template literal types
Tests type-level string composition with template literals. Good answer: define a Resource union, interpolate it into /api/v1/ paths, then union with the /{id} variant. Red flag: suggesting runtime regex or plain string instead of literal types.
WHAT THIS TESTS: Your fluency with TypeScript type-level programming, specifically template literal types and the way they distribute over union types. The interviewer wants to see if you view the type system as a value-level programming environment for strings, and whether you understand that placing a union inside an interpolated position causes TypeScript to expand the template into every possible string literal permutation. They also care if you know the difference between compile-time string manipulation and runtime validation.
A GOOD ANSWER COVERS: First, define a finite union of valid resource names such as type Resource = "users" | "products" | "orders". Second, interpolate that union into a template literal to create the base path: type BaseRoute = the literal /api/v1/ plus Resource in an interpolated slot. Because the interpolated position contains a union, TypeScript automatically expands this into a set of concrete string literals such as /api/v1/users and /api/v1/products. Third, model the single-resource endpoint by creating another set of strings that appends /{id} to each base route, then union the collection paths and item paths together: type ApiRoute = BaseRoute or BaseRoute with /{id} appended. Fourth, explicitly state that any string not present in these literal unions is rejected at compile time, which gives you the typesafe API client behavior.
COMMON WRONG ANSWERS: Proposing runtime regex validation inside the API client instead of solving the problem at the type level. Using the plain string type or an overly broad template that accepts any string in the resource slot. Forgetting that the /{id} variant must be explicitly included in the final union rather than assumed. Suggesting numeric enums or const enums instead of string literal unions, which adds indirection without benefit. Claiming that mapped types, conditional types, or recursive types are necessary for this construction, when simple template literal expansion over a union is the idiomatic solution.
LIKELY FOLLOW-UPS: How would you restrict the id segment so it only accepts numeric strings rather than arbitrary strings? How would you model nested resources such as /api/v1/users/{userId}/orders? What are the compiler performance implications if the Resource union grows to hundreds or thousands of members, and when should you switch to ahead-of-time generation? Can you write a conditional type that infers the resource name and id back out from a given ApiRoute? How would you map each valid ApiRoute to a specific JSON response type so the client return type is also fully typed?
ONE CONCRETE EXAMPLE: type Resource = "users" | "products" | "orders". type BaseRoute = the literal /api/v1/ plus Resource in an interpolated position. type ApiRoute = BaseRoute or BaseRoute/{id}. When you declare a variable of type ApiRoute, it happily accepts /api/v1/users and /api/v1/products/{id}, but the compiler rejects /api/v1/customers because customers is not a member of the Resource union, giving you immediate static feedback.
Read the original → typescriptlang.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.