如何用TypeScript类型语法表达按需返回字段的Web API响应?
Great question! This is a super common scenario when working with APIs that let you pick and choose which fields to return, and TypeScript’s generic and mapped types are perfect for modeling this behavior. Here’s a step-by-step solution:
First, let’s split your API data into two distinct parts: fields that are always present no matter what, and fields that are optional (only returned if explicitly requested).
Step 1: Define Base Types
Start by creating types for both categories of fields:
// Fields that require explicit request to be included in the response type OptionalApiFields = { a: string; b: string; c: string; }; // Fields that are always included in every API response type MandatoryApiFields = { d: string; };
Step 2: Build a Generic Response Type
Next, we’ll create a generic type that combines the requested optional fields with the mandatory ones. We use Pick to select only the requested fields from OptionalApiFields, then intersect that with the mandatory fields to ensure they’re always present:
// Generic type that adapts based on which optional fields you request type ApiResponse<T extends keyof OptionalApiFields = never> = Pick<OptionalApiFields, T> & MandatoryApiFields;
The = never default means if you don’t request any optional fields, the response will only include the mandatory d field.
Step 3: Type the API Function
Now we can type your getResponse function to infer the requested fields and return the correct response type automatically:
// Mock implementation of your API call (adjust for real use) function getResponse<T extends keyof OptionalApiFields>( fields: T[] ): ApiResponse<T> { // Full dataset the API has available const fullData = { a: "hello", b: "world", c: "of goo", d: "i am always present" }; // Start with mandatory fields, then add requested optional ones const filteredResponse = { d: fullData.d } as ApiResponse<T>; fields.forEach(field => { filteredResponse[field] = fullData[field]; }); return filteredResponse; }
Step 4: See It in Action
Let’s test this to confirm the type safety works as expected:
// Request fields "a" and "b" const response1 = getResponse(["a", "b"]); // Type: { a: string; b: string; d: string } console.log(response1.a); // "hello" console.log(response1.b); // "world" console.log(response1.d); // "i am always present" // @ts-expect-error: Property 'c' doesn't exist here (we didn't request it!) console.log(response1.c); // Request no optional fields const response2 = getResponse([]); // Type: { d: string } console.log(response2.d); // "i am always present" // @ts-expect-error: "a" isn't included here console.log(response2.a);
Bonus: Flexible Input
If you want to support passing a single field instead of an array (e.g., getResponse("a")), adjust the function signature slightly:
function getResponse<T extends keyof OptionalApiFields>( fields: T | T[] = [] ): ApiResponse<T> { const fieldList = Array.isArray(fields) ? fields : [fields]; // Rest of the implementation stays the same }
This setup gives you full type safety—TypeScript will autocompletable fields based on your request, and flag any attempts to access fields you didn’t ask for.
内容的提问来源于stack exchange,提问作者DanielOfTaebl

