volks
volks3y ago

Losing the type between the client and server

Hello everyone, I am new to tRPC and the magic of types in typescript so I am looking for ideas as to what is happening, the client is receiving any type instead of a string
11 Replies
volks
volksOP3y ago
I doubt this is a bug, but I have no idea where to go for. This is on the client
(property) onSuccess?: ((data: {
id: any;
exists: any;
name: any;
email: string;
}, variables: {
provider?: any;
presignupEmail?: string | undefined;
email: string;
userID: string;
}, context: unknown) => unknown) | undefined
(property) onSuccess?: ((data: {
id: any;
exists: any;
name: any;
email: string;
}, variables: {
provider?: any;
presignupEmail?: string | undefined;
email: string;
userID: string;
}, context: unknown) => unknown) | undefined
And this is on the server
(method) ProcedureBuilder<{ _config: RootConfig<{ ctx: { user?: undefined; } | { user: { uid: string; email: string; }; }; meta: {}; errorShape: DefaultErrorShape; transformer: CombinedDataTransformer; }>; ... 5 more ...; _output_out: typeof unsetMarker; }>.mutation<{
id: string;
exists: boolean;
name: string | undefined;
email: string;
}>(resolver: (opts: ResolveOptions<{
_config: RootConfig<{
ctx: {
user?: undefined;
} | {
user: {
uid: string;
email: string;
};
};
meta: {};
errorShape: DefaultErrorShape;
transformer: CombinedDataTransformer;
}>;
... 5 more ...;
_output_out: typeof unsetMarker;
}>) => MaybePromise<...>): BuildProcedure<...>
Mutation procedure
(method) ProcedureBuilder<{ _config: RootConfig<{ ctx: { user?: undefined; } | { user: { uid: string; email: string; }; }; meta: {}; errorShape: DefaultErrorShape; transformer: CombinedDataTransformer; }>; ... 5 more ...; _output_out: typeof unsetMarker; }>.mutation<{
id: string;
exists: boolean;
name: string | undefined;
email: string;
}>(resolver: (opts: ResolveOptions<{
_config: RootConfig<{
ctx: {
user?: undefined;
} | {
user: {
uid: string;
email: string;
};
};
meta: {};
errorShape: DefaultErrorShape;
transformer: CombinedDataTransformer;
}>;
... 5 more ...;
_output_out: typeof unsetMarker;
}>) => MaybePromise<...>): BuildProcedure<...>
Mutation procedure
julius
julius3y ago
Do you have strict mode? Are you using any data transformers?
volks
volksOP3y ago
Hey @julius, I am using strict mode and I am using superjson. I am currently debugging on a reproducable repo first, but with a stripped down setup the types are working, so I am super confused at the moment Do you maybe have any pointers at the top of your head that can break types between projects. I basically have a monorepo
apps/
- backend with trpc that exports app router
- nextjs site that imports the app router
apps/
- backend with trpc that exports app router
- nextjs site that imports the app router
I just import the type from ../apps/backend/src/trpc.ts I'll keep debugging and close this if I find the culprit
julius
julius3y ago
Not really, is the repo public?
volks
volksOP3y ago
I have a minimal repository that closely mimics my real project which is unfortunately commercial and private, but I cannot for the life of me replicate the issue I am having in the real project @julius For example
export const sessionContext = async ({
req,
}: NodeHTTPCreateContextFnOptions<IncomingMessage, ServerResponse>) => {
try {
if (
req.headers.authorization &&
req.headers.authorization.split(" ")[0] === "Bearer"
) {
const token = req.headers.authorization.split(" ")[1] as string;
const decodedToken = await firebaseAdmin.auth().verifyIdToken(token);

if (!decodedToken.email) {
return {};
}

return {
user: {
uid: decodedToken.uid,
email: decodedToken.email,
},
};
}
return {};
} catch (error) {
return {};
}
};

export type Context = inferAsyncReturnType<typeof sessionContext>;
export const sessionContext = async ({
req,
}: NodeHTTPCreateContextFnOptions<IncomingMessage, ServerResponse>) => {
try {
if (
req.headers.authorization &&
req.headers.authorization.split(" ")[0] === "Bearer"
) {
const token = req.headers.authorization.split(" ")[1] as string;
const decodedToken = await firebaseAdmin.auth().verifyIdToken(token);

if (!decodedToken.email) {
return {};
}

return {
user: {
uid: decodedToken.uid,
email: decodedToken.email,
},
};
}
return {};
} catch (error) {
return {};
}
};

export type Context = inferAsyncReturnType<typeof sessionContext>;
In my main project it has any types like displayed bellow but in my demo project it correctly has the uid and email as strings
(alias) type AppRouter = Router<RouterDef<RootConfig<{
ctx: {
user?: undefined;
} | {
user: {
uid: any;
email: any;
};
};
}>, {
...;
}, {
...;
}>> & {
...;
}
(alias) type AppRouter = Router<RouterDef<RootConfig<{
ctx: {
user?: undefined;
} | {
user: {
uid: any;
email: any;
};
};
}>, {
...;
}, {
...;
}>> & {
...;
}
So I thought that the types might be incorrect all together but if I do
export const sessionContext = async ({
req,
}: NodeHTTPCreateContextFnOptions<IncomingMessage, ServerResponse>) => {
return {
user: {
uid: "test",
email: "test",
},
};
};

export type Context = inferAsyncReturnType<typeof sessionContext>;
export const sessionContext = async ({
req,
}: NodeHTTPCreateContextFnOptions<IncomingMessage, ServerResponse>) => {
return {
user: {
uid: "test",
email: "test",
},
};
};

export type Context = inferAsyncReturnType<typeof sessionContext>;
It gets inferred to
(alias) type AppRouter = Router<RouterDef<RootConfig<{
ctx: {
user: {
uid: string;
email: string;
};
};
}
(alias) type AppRouter = Router<RouterDef<RootConfig<{
ctx: {
user: {
uid: string;
email: string;
};
};
}
The type is correct on the server but gets inferred to any in the client But as you can see the types do get across Just some are any I have absolutely no clue as to what is happening Also the inferred type of my mutation has the same issue
type test = {
id: any;
exists: any;
name: any;
email: string;
}
type test = {
id: any;
exists: any;
name: any;
email: string;
}
But on the server the types are all okay
(method) ProcedureBuilder<{ _config: RootConfig<{ ctx: { user?: undefined; } | { user: { uid: string; email: string; }; }; meta: {}; errorShape: DefaultErrorShape; transformer: CombinedDataTransformer; }>; ... 5 more ...; _output_out: typeof unsetMarker; }>.mutation<{
id: string;
exists: boolean;
name: string | undefined;
email: string;
}>(resolver: (opts: ResolveOptions<{
(method) ProcedureBuilder<{ _config: RootConfig<{ ctx: { user?: undefined; } | { user: { uid: string; email: string; }; }; meta: {}; errorShape: DefaultErrorShape; transformer: CombinedDataTransformer; }>; ... 5 more ...; _output_out: typeof unsetMarker; }>.mutation<{
id: string;
exists: boolean;
name: string | undefined;
email: string;
}>(resolver: (opts: ResolveOptions<{
julius
julius3y ago
might be invalid tsconfig? or do you use the same in the repro?
volks
volksOP3y ago
Both are the same, strict mode is on But the types are getting through to the client, just for some reason some types are lost and inferred to any Thats why I am super confused
julius
julius3y ago
yea if you cant provide a repro i really cant help you since ive never seen this before
volks
volksOP3y ago
Yeah I'll try to get it reproduced and push it to gh
volks
volksOP3y ago
Hey @julius, tried my best to create a simple reproducible repository that reflects my issue. I added a readme that clarifies the issue further, here is the repo https://github.com/Nikola-Milovic/monorepo-trpc-issue I know this is a tall ask to check out but thank you nonetheless
GitHub
GitHub - Nikola-Milovic/monorepo-trpc-issue
Contribute to Nikola-Milovic/monorepo-trpc-issue development by creating an account on GitHub.
volks
volksOP3y ago
Inside packages/config are the tsconfig files and they all inherit from the base.json which defines the strict: true