TypeScript:克隆现有接口并递归转换属性为string类型(含数组特殊处理)
You can absolutely pull this off with pure TypeScript type definitions! The core is building a recursive type that handles primitive values, nested objects, and arrays separately. Here's the complete working solution:
// Helper type to recursively transform types into string-based structures type DeepStringify<T> = // Handle arrays: transform the element type instead of retaining the array structure T extends Array<infer U> ? DeepStringify<U> : // Handle objects: recursively process every nested property T extends object ? { [K in keyof T]: DeepStringify<T[K]> } : // Convert all primitive types directly to string string; // Your desired CloneType, applying the deep string transformation to every property of the input model type CloneType<Model> = { [K in keyof Model]: DeepStringify<Model[K]>; };
How This Logic Works
Let’s break down each part of the solution:
- Array Handling: The first conditional checks if the type is an array (
T extends Array<infer U>). Usinginfer Uextracts the array's element type, then we applyDeepStringifyto that element. This is why youritemsproperty (originallyItem[]) becomes the stringified version ofIteminstead of an array of strings. - Object Handling: The second conditional targets objects (arrays are already handled earlier, so we don’t reprocess them here). It creates a mapped type where every property of the object is recursively transformed with
DeepStringify. - Primitive Handling: Any type that’s not an array or object (like
number,boolean, etc.) gets converted straight tostring.
Testing with Your Example
When applied to your original interfaces, this produces exactly the type you need:
interface A { innerA: InnerA; innerB: InnerB; } interface InnerA { items: Item[]; name: string; counter: number; } interface InnerB { address: string; code: number; } interface Item { id: number; } const labels: CloneType<A> = { innerA: { items: { id: 'ID' }, name: 'Some Name', counter: 'Some Counter' }, innerB: { address: 'Address', code: 'Postal Code' } }; // These type checks will work flawlessly const postalCodeLabel: string = labels.innerB.code; const itemIdLabel: string = labels.innerA.items.id;
What Was Missing in Your Original Attempt
Your initial CloneType only handled one level of nesting and didn’t account for arrays. It assumed every property was an object and mapped its fields to string, but didn’t recursively process deeper nested objects or handle the special array case you needed. The recursive DeepStringify helper fixes both of these gaps.
内容的提问来源于stack exchange,提问作者MrrrrMeowCat

