Is it true that TypeScript types on server action parameters don't mean anything?
Unanswered
Havana posted this in #help-forum
HavanaOP
Take this:
Is my above comment true? Does it mean that the only correct type for server action parameters is
I created a helper type that does this and still maintains what the intent of the parameter type was while maintaining type safety/security:
And use it like this:
I don't see another way.
// actions.ts
"use server"
export function createUser(age: number) {
// age can be anything here, even a string, because it can be called from anywhere (like a public API endpoint). So it being a number is a FALSE statement and a LIE. Or does Nextjs validate its type?
}Is my above comment true? Does it mean that the only correct type for server action parameters is
age: unknown?I created a helper type that does this and still maintains what the intent of the parameter type was while maintaining type safety/security:
/**
* "Never trust the client."
*
* Specifies a parameter that can be passed by anyone to the server.
*/
export type OpenServerParameter<T> = T | unknownAnd use it like this:
// actions.ts
"use server"
export async function restoreFolders(
backupId: OpenServerParameter<string>,
folderPaths: OpenServerParameter<NonEmptyArray<string>>,
destination: OpenServerParameter<"default" | "original">,
) {I don't see another way.
12 Replies
@Havana Take this:
// actions.ts
"use server"
export function createUser(age: number) {
// age can be anything here, even a string, because it can be called from anywhere (like a public API endpoint). So it being a number is a FALSE statement and a LIE. Or does Nextjs validate its type?
}
Is my above comment true? Does it mean that the only correct type for server action parameters is `age: unknown`?
I created a helper type that does this and still maintains what the intent of the parameter type was while maintaining type safety/security:
/**
* "Never trust the client."
*
* Specifies a parameter that can be passed by anyone to the server.
*/
export type OpenServerParameter<T> = T | unknown
And use it like this:
// actions.ts
"use server"
export async function restoreFolders(
backupId: OpenServerParameter<string>,
folderPaths: OpenServerParameter<NonEmptyArray<string>>,
destination: OpenServerParameter<"default" | "original">,
) {
I don't see another way.
that's everywhere like that. Types don't say "here is in every case this specific type of value". The value can be everytime different
@B33fb0n3 that's everywhere like that. Types don't say "here is in every case this specific type of value". The value can be everytime different
HavanaOP
What do you mean? Can you elaborate?
Of course users can intentionally "lie" like this:
function createUser(age: number) {}
createUser("abc" as unknown as number) // compilesBut this requires the developer to explicitly lie
Or better yet, it requires the CALLER to explicitly lie. If you look at my original post, there is no explicit lying by the caller. At least not in the source code of the project.
the types from typescript are just there to define your schema of specific objects. But the objects at the end, can still be different values in runtime
HavanaOP
Normally, only if you have explicit "lies" in your codespace like the example above.
And true, still a person in the browser can call your functions the way he wants
It is just, now with nextjs being server side and all, such lies can be dangerous
With client side, you could have just said indeed createUser(age: number), and if some guy calls it in the browser console with a string, then let it just break, he just self sabotages his own app experience.
But with server, we make assumptions on the validity of data. It's unfortunate if we can't trust TypeScript's types anymore, it is less productive.
But with server, we make assumptions on the validity of data. It's unfortunate if we can't trust TypeScript's types anymore, it is less productive.
@Havana With client side, you could have just said indeed createUser(age: number), and if some guy calls it in the browser console with a string, then let it just break, he just self sabotages his own app experience.
But with server, we make assumptions on the validity of data. It's unfortunate if we can't trust TypeScript's types anymore, it is less productive.
that's what I am saying. You shouldn't trust typescript types at all. They are just there to see errors while developement. The runtime variables can be completelly different (of course they are normally not, but they can). So, don't trust types, just because you said a specific variable is a string or a number or whatever. Verify if they are really the value that you expect, if the type is very important
Gazami crab
TypeScript is technically a "transpiled" (source-to-source translation) language. What you write is not what you're running. JavaScript has limited runtime type safety.
Check out https://zod.dev/ if you'd like to add data validation at runtime. For example, in your
Check out https://zod.dev/ if you'd like to add data validation at runtime. For example, in your
createUser(age: number) function, you could run z.number().positive().parse(age). What's really great is you can define Zod schemas and use them on the server and in frameworks like React Hook Form https://github.com/react-hook-form/resolvers so that you also get client-side validation in your forms from the same schema code. Note also that Zod supports type Something = z.infer<typeof SomethingSchema>, allowing you to define your application's types with a schema and then infer the TypeScript typings.