-
I have the following types to implement on-demand data retrieval (similar to GraphQL): /**
* Array selection descriptor for arrays.
* Tuple of [item selection, pagination options].
* - select: selection applied to each item
* - option.offset: 0-based start index
* - option.length: number of items to include (optional)
* @template TBaseItem Item type of the source array
*/
export type ArraySelect<TBaseItem> = [
select: Select<TBaseItem>,
option?: {
/** 0-based start index */
offset: number;
/** Number of items to include (to end if omitted) */
length?: number;
},
];
/** Select all fields at current depth (shallow) */
export type SelectAll = "*";
/** Select all fields recursively (deep) */
export type SelectDeepAll = "**";
/**
* Flexible selection type depending on the base type:
* - Array → ArraySelect of its item type
* - Object → partial map of keys to nested Select, or wildcards "*" / "**"
* - Scalar → true
* @template TBase Base type to select from
*/
export type Select<TBase> = (TBase extends Date ? string : TBase) extends object
// Is Object
? TBase extends Array<infer TBaseItem>
// Is Array
? ArraySelect<TBaseItem>
// Is Object, But Not Array
:
| Partial<{ [TKey in keyof TBase]: Select<TBase[TKey]> }>
| SelectAll
| SelectDeepAll
// Is Scalar
: true;
/**
* Result type for array selections: an array of selected item results.
* @template TBaseItem Item type of the source array
* @template TSelect Array selection applied to items
*/
export type ArrayResult<
TBaseItem,
TSelect extends ArraySelect<TBaseItem>,
> = Result<TBaseItem, TSelect[0]>[];
/**
* Transform TBase into the shape described by TSelect.
* Rules:
* - Array → ArrayResult of item Result using the item selection (tuple[0])
* - Object + "*" → Shallow<TBase>
* - Object + "**" → TBase (deep copy)
* - Object + field map → object with only selected keys, each recursively transformed
* - Scalar → TBase
* @template TBase Base type to transform
* @template TSelect Selection to apply
*/
export type Result<TBase, TSelect extends Select<TBase>> = TBase extends object
// Is Object
? TBase extends Array<infer TBaseItem>
// Is Array -> ArrayResult
? ArrayResult<
TBaseItem,
TSelect extends ArraySelect<TBaseItem> ? TSelect : never
>
// Is Object, But Not Array
: TSelect extends SelectAll
// Select All (Shallow)
? Shallow<TBase>
: TSelect extends SelectDeepAll
// Select Deep All
? TBase
: {
[
K in keyof TSelect as K extends keyof TBase
? TSelect[K] extends Select<TBase[K]> ? K
: never
: never
]: K extends keyof TBase
// K Is In TBase
? TSelect[K] extends Select<TBase[K]>
// TSelect[K] Is Select
? Result<TBase[K], TSelect[K]>
: never
: never;
}
// Is Scalar
: TBase; However, this requires the type of select to determine the type of result, like: type get = <TSelect extends Select<Base>>(select: TSelect) => Result<Base, TSelect> Currently, oRPC seems unable to do this? |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
Hi @NMNMCC! I'm Dosu and I’m helping the orpc team. oRPC does not currently support generic procedures where the output type is inferred from the input type parameter, such as the select/result pattern you described. The framework defines API contracts using static input and output schemas (often with zod), and TypeScript's type inference limitations prevent propagating arbitrary generic relationships from input to output through oRPC's abstraction layers. There are workarounds using direct function types, but these break features like runtime validation and OpenAPI generation. This is a known challenge and limitation in the codebase, and while there are aspirations and partial attempts to support this pattern, it's not natively possible at the moment see contract README. To reply, just mention @dosu. How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other |
Beta Was this translation helpful? Give feedback.
-
Coincidentally, there was also a user on Discord who posted a similar question at almost the same time. https://discord.com/channels/1308966753044398161/1409453220010524702 |
Beta Was this translation helpful? Give feedback.
-
In short, we can’t make something generic if it’s already generic, like a procedure |
Beta Was this translation helpful? Give feedback.
In short, we can’t make something generic if it’s already generic, like a procedure