Description
Suggestion
It would be nice to be able to infer all variants from a recursive type. Playground link.
// SETUP
// E.g. for nested selectable lists
type Recursive<Id> = {
id: Id
children: readonly Recursive<Id>[]
}
// Function to return all ids from the type
function getIds<Id>(items: readonly Recursive<Id>[]): Id[] {
return [] // return value is not important
}
// EXAMPLE USAGE
const items = [{
id: 'a',
children: [{
id: 'b',
children: []
}]
}] as const satisfies readonly Recursive<string>[]
const foo = getIds(items)
// EXPECTED: ("a" | "b")[] but returns just "a"[]
🔍 Search Terms
recursive types
✅ Viability Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
It might be breaking from types perspective if some code relies on the current behaviour. - This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
⭐ Suggestion
Generics inside recursive types would be expanded.
📃 Motivating Example
E.g. for selectable nested lists, we could provide auto-complete for onSelect(itemId: <union of all ids from recursive type>)
.
💻 Use Cases
I haven't found a workaround that would work for the getIds
function in the example. One semi working workaround is written below - it returns correctly union of all ids literals, but TS shows an error and it cannot be used as return value for getIds(...)
because then TS show that the type is recursive and possible infinite.
type GetRecursiveType<T extends readonly Recursive<any>[]> = T[number]['id'] | (T[number]['children'] extends undefined ? never : GetRecursiveType<T[number]['children']>)