Skip to content

Commit e147412

Browse files
mateus-pymc9
andauthored
feat: flexible 'createRouter' typings (#654)
Co-authored-by: Yiming <yiming@whimslab.io>
1 parent 76c12f5 commit e147412

File tree

2 files changed

+78
-11
lines changed

2 files changed

+78
-11
lines changed

packages/plugins/trpc/src/generator.ts

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,35 +89,85 @@ function createAppRouter(
8989
const prismaImport = getPrismaClientImportSpec(zmodel, path.dirname(indexFile));
9090
appRouter.addImportDeclarations([
9191
{
92-
namedImports: ['AnyRootConfig'],
92+
namedImports: ['type AnyRootConfig', 'type Procedure', 'type ProcedureParams', 'type ProcedureType'],
9393
moduleSpecifier: '@trpc/server',
9494
},
9595
{
96-
namedImports: ['PrismaClient'],
96+
namedImports: ['type PrismaClient', 'type Prisma'],
9797
moduleSpecifier: prismaImport,
98-
isTypeOnly: true,
9998
},
10099
{
101-
namedImports: ['createRouterFactory', 'AnyRouter'],
100+
namedImports: ['type createRouterFactory', 'AnyRouter'],
102101
moduleSpecifier: '@trpc/server/dist/core/router',
103102
},
104103
{
105-
namedImports: ['createBuilder'],
104+
namedImports: ['type ProcedureBuilder'],
106105
moduleSpecifier: '@trpc/server/dist/core/internals/procedureBuilder',
107106
},
107+
{ defaultImport: 'z', moduleSpecifier: 'zod', isTypeOnly: true },
108108
]);
109109

110110
appRouter.addStatements(`
111+
${/** to be used by the other routers without making a bigger commit */ ''}
112+
export { PrismaClient } from '${prismaImport}';
113+
111114
export type BaseConfig = AnyRootConfig;
112115
113116
export type RouterFactory<Config extends BaseConfig> = ReturnType<
114117
typeof createRouterFactory<Config>
115118
>;
116119
117-
export type ProcBuilder<Config extends BaseConfig> = ReturnType<
118-
typeof createBuilder<Config>
119-
>;
120+
${
121+
/** this is needed in order to prevent type errors between a procedure and a middleware-extended procedure */ ''
122+
}
123+
export type ProcBuilder<Config extends BaseConfig> = ProcedureBuilder<{
124+
_config: Config;
125+
_ctx_out: Config['$types']['ctx'];
126+
_input_in: any;
127+
_input_out: any;
128+
_output_in: any;
129+
_output_out: any;
130+
_meta: Config['$types']['meta'];
131+
}>;
132+
133+
type ExtractParamsFromProcBuilder<Builder extends ProcedureBuilder<any>> =
134+
Builder extends ProcedureBuilder<infer P> ? P : never;
120135
136+
type FromPromise<P extends Promise<any>> = P extends Promise<infer T>
137+
? T
138+
: never;
139+
140+
${/** workaround to avoid creating 'typeof unsetMarker & object' on the procedure output */ ''}
141+
type Join<A, B> = A extends symbol ? B : A & B;
142+
143+
${
144+
/** you can name it whatever you want, but this is to make sure that
145+
the types from the middleware and the procedure are correctly merged */ ''
146+
}
147+
export type ProcReturns<
148+
PType extends ProcedureType,
149+
PBuilder extends ProcBuilder<BaseConfig>,
150+
ZType extends z.ZodType,
151+
PPromise extends Prisma.PrismaPromise<any>
152+
> = Procedure<
153+
PType,
154+
ProcedureParams<
155+
ExtractParamsFromProcBuilder<PBuilder>["_config"],
156+
ExtractParamsFromProcBuilder<PBuilder>["_ctx_out"],
157+
Join<ExtractParamsFromProcBuilder<PBuilder>["_input_in"], z.infer<ZType>>,
158+
Join<ExtractParamsFromProcBuilder<PBuilder>["_input_out"], z.infer<ZType>>,
159+
Join<
160+
ExtractParamsFromProcBuilder<PBuilder>["_output_in"],
161+
FromPromise<PPromise>
162+
>,
163+
Join<
164+
ExtractParamsFromProcBuilder<PBuilder>["_output_out"],
165+
FromPromise<PPromise>
166+
>,
167+
ExtractParamsFromProcBuilder<PBuilder>["_meta"]
168+
>
169+
>;
170+
121171
export function db(ctx: any) {
122172
if (!ctx.prisma) {
123173
throw new Error('Missing "prisma" field in trpc context');
@@ -233,7 +283,14 @@ function generateModelCreateRouter(
233283

234284
modelRouter.addImportDeclarations([
235285
{
236-
namedImports: ['type RouterFactory', 'type ProcBuilder', 'type BaseConfig', 'db'],
286+
namedImports: [
287+
'type RouterFactory',
288+
'type ProcBuilder',
289+
'type BaseConfig',
290+
'type ProcReturns',
291+
'type PrismaClient',
292+
'db',
293+
],
237294
moduleSpecifier: '.',
238295
},
239296
]);

packages/plugins/trpc/src/helpers.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,24 @@ export function generateProcedure(
2121
writer.write(`
2222
${opType}: procedure.input(${typeName}).query(({ctx, input}) => checkRead(db(ctx).${lowerCaseFirst(
2323
modelName
24-
)}.${prismaMethod}(input as any))),
24+
)}.${prismaMethod}(input as any))) as ProcReturns<
25+
"query",
26+
Proc,
27+
(typeof ${upperCaseFirst(modelName)}InputSchema)["${opType.replace('OrThrow', '')}"],
28+
ReturnType<PrismaClient["${lowerCaseFirst(modelName)}"]["${opType}"]>
29+
>,
2530
`);
2631
} else if (procType === 'mutation') {
2732
// the cast "as any" is to circumvent a TS compiler misfired error in certain cases
2833
writer.write(`
2934
${opType}: procedure.input(${typeName}).mutation(async ({ctx, input}) => checkMutate(db(ctx).${lowerCaseFirst(
3035
modelName
31-
)}.${prismaMethod}(input as any))),
36+
)}.${prismaMethod}(input as any))) as ProcReturns<
37+
"mutation",
38+
Proc,
39+
(typeof ${upperCaseFirst(modelName)}InputSchema)["${opType.replace('OrThrow', '')}"],
40+
ReturnType<PrismaClient["${lowerCaseFirst(modelName)}"]["${opType}"]>
41+
>,
3242
`);
3343
}
3444
}

0 commit comments

Comments
 (0)