We were really excited to build an app w/ shared types between front-end and back-end, particularly for validation. But we've run into some complications. Originally we had a 3rd repository for types (auto-generated from Open API) and validation logic, but this led to a slow and fragile development process. We couldn't move to a monorepo, so we now have this code live in the back-end repo and copy what we need to the front-end (not ideal). There's also the issue of the validation library: what works best for the back-end (Nest.js) is not necessarily a great tool for the UI (Vue.js). Not only do the rules not always line up perfectly, the library we chose has no tools for properly displaying/clearing error messages as the user interacts with the form. If I were to do it all over again, I would set up the code in a mono-repo and use separate validation libraries (but with shared types) between the back-end and front-end.
I don't think that End-To-End Typesafety is a good idea from a design perspective. In real world applications your backend models most likely have more data fields than your frontend models. e.g. on the backend your users have a password and maybe a birthday field. Things you don't wanna expose to your frontend. So your frontend User has different fields.
This is why there should be Database Models, Server Models, Web Models, Client Models DB Models = what is actually stored in the DB Server Models = what the server itself deals with.. Web Models = usually a request and response variety, this is what you would see in open-api specification. Client Models = what the front end will use.. Now, if they all happen to be 1:1 with each other (so I guess 1:1:1:1 hehe) then all the better.. But there will be times when servers change slightly, the web requests have additional fields, or maybe even fields that are not super convenient for the front end.. This is especially useful when you have a proper size application, and not just a tiny hobby sized project.
One nice thing about TypeScript though is that with utility types you could potentially modify types so that your UI types could add/remove properties from the server type, but you still retain some consistency between the two.
We always map to some kind of DTO. Then you can control exactly what's coming in and out of your server. Your response DTO's can exactly match the model you need for your UI.