Typescript not validating included relation
This is likely due to my inexperience with typescript, but I've been trying to access a related property that I've included on a query, but for some reason typescript isn't validating that it's a valid property. I think it only has scalar properties available.
gives this error:
I'd expect prisma and/or wasp handles the types when i run
wasp db migrate-dev
, is that not correct?16 Replies
Hi @wardbox. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
The issue you're experiencing is related to full-stack type safety in Wasp. When you annotate your function with a type, like
GetTeamById<{ id: string }, Team>
, it tells TypeScript that the function will return an object of type Team
. However, the Team
type only includes its scalar properties and not the relationships.
In your case, logo
is a related property, not a scalar property, so TypeScript doesn't recognize it as part of the Team
type. This is why you're getting the error Property 'logo' does not exist on type 'GetResult<{ id: string; name: string; createdAt: Date; }, unknown> & {}
.
To resolve this issue, you need to create a new type that includes the logo
property and use that type to annotate your function. Unfortunately, as of now, Wasp doesn't automatically generate types that include related properties when you run wasp db migrate-dev
.
This issue has been discussed in the Wasp community before. You can refer to this Discord thread (https://discord.com/channels/686873244791210014/1217160369789735062) and this Github issue (https://github.com/wasp-lang/wasp/issues/901) for more information.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @wardbox, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?This works on the component where useQuery is run, but when I pass that down to another component i have no way of importing the type with logo added :think_cry:
Wohooo @wardbox, you just became a Waspeteer level 1!
as a workaround, currently exporting the type at the level where usequery is run and importing it in subcomponents
@sodic has a good insight with using
satisfies
that could help you@miho Thanks for calling me in ๐
@wardbox, to avoid defining a new type for the Query's return value, use TypeScript's
satisfies
keyword.
This tells TypeScript: "Make sure this function doesn't take any arguments and works with the proper context object, but infer the correct return type on your own."
The satisfies
keyword isn't Wasp-specific. It's a feature of TypeScript, so make sure to add it to your TS knowledge base and take it with you wherever you go ๐
In your case, the function should probably look like this:
@sodic maybe we can remove the
Team
as return value or it will infer the correct shape even with the Team
return value?We can, but keeping it makes sure that you always return something that satisfies the
Team
constraint
So, @wardbox, it's up to you to choose what you want to infer and what you want to constrain explicitlyBtw, our docs used to mention this, but we removed it thinking it was too much of an edge case. Looks like we'll be adding it back in after all (issue here) ๐
Here's the relevant section from the old docs:
GitHub
Issues ยท wasp-lang/wasp
The fastest way to develop full-stack web apps with React & Node.js. - Issues ยท wasp-lang/wasp
That works wherever I call the useQuery function but is there anything special I need to do when I pass this down to another component? If i have something like this for example it isn't aware of the usequery return value
team.tsx
teamprofile.tsx
team is still like this, i presume from importing the entity
i suppose i could just store in state and pass that around instead?Again, this is not Wasp specific. When you declare something as type
X
, TypeScript will treat it as type X
.
You declared the team
prop as being of type Team
. Therefore, TypeScript thinks it's a Team
(and nothing else).
If you want to use the query's return type (that's more than just a Team
) as the type of the prop, there's two ways to do it:
- Option 1: Use the Query's return type as a source of truth and rely on type inference for typing the client stuff.
- Option 2: Explicitly define the payload type in a single place and import/reuse it on both the client and the server.
Option 1
Extract the inferred return type on the frontend and use it there:
This way, the Query's inferred return type becomes the source of truth for what you pass around in your components (and changes depending on what you do in the Query).
Option 2
This is what you already did in this message: https://discord.com/channels/686873244791210014/1233163179140321362/1233181933496832135
It all comes down to what you prefer:
- Option 1 is "let typescript infer all the types and do the checking"
- Option 2 is "I want to explicitly state the types I'm working with"
If you go with Option 2, you can ditch the whole satisfies
story and just explicitly define your payload type (that's richer than just a Team
)thanks - option 1 sounds easier, first time using typing and it is so nice to have everything typed but i kinda dove in so figuring these details out as i go!
I prefer Option 1 as well. It looks a little wild, but once you set it up, you just code and forget about the types
And the compiler warns you when you mess up ๐
Absolutely, all I was aiming for was avoid having to update types in multiple places, that achieves what I wanted!
Glad I could help!
Let me know if you have trouble with setting this up, I can create a gist to help you out if necessary.
I've added this message to the issue that talks about reincluding this into the docs: https://github.com/wasp-lang/wasp/issues/1920
GitHub
Reinclude lost TypeScript documentation ยท Issue #1920 ยท wasp-lang/w...
We used to have TS docs in typescript.md, but the file got removed: https://github.com/wasp-lang/wasp/pull/1633/files#diff-3133a1d4afca962779005e99886a9ee9cc139de5e5e39b19573528098931f7f1. We lost ...
got it working :ty2: