Is there any way to mark Generics as mandatory in typescript?
function onSubmit<Result, Variables>({
foo,
bar
}: {
foo: Result
bar: Variables
}) {
console.log(foo, bar)
}
// this should not be possible without passing the Generics
onSubmit({ foo: 'foo', bar: 'bar' })
// this works as expected
onSubmit<number, number>({ foo: 'foo', bar: 'bar' })
Playground
The question was answered and it works like a charm. To better understand why I want to use this, here's an example.
onSubmit<UpdateUserMutation, UpdateUserMutationVariables>({
mutation: UpdateUserDocument,
variables: { id: user.id, user: formData },
refetchQueries: ['GetUserList'],
onSuccess: ({ data }) => history.push(`/users/${data.updateUser.id}`),
})
When I omit <UpdateUserMutation, UpdateUserMutationVariables>, I don't know what data will be in my onSuccess function. Additionally, I don't know which variables I have to pass to the variableskey. Not to mention the IDE integration :-)
For sure I could type { data } directly, but I think now it's way more comfortable, because I can't "forget" to type and so I will never use wrong/forget to use variables in the variables object and the onSuccess function.
For the curious one's, this is how my onSubmit function looks now:
function onSubmit<
Result = [Error, 'Please specify the Result type parameter'],
Variables = [Error, 'Please specify the Variables type parameter'],
R extends Result = Result,
V extends Variables = Variables
>({
mutation,
variables,
refetchQueries,
onSuccess,
}: {
mutation: DocumentNode
variables: V
refetchQueries: ((string | PureQueryOptions)[]) | (string | PureQueryOptions)[]
onSuccess: (response: FetchResult<R, Record<string, any>, Record<string, any>>) => void
}) {
client
.mutate<R>({
mutation,
variables,
refetchQueries,
})
.then(response => {
setErrors({})
onSuccess(response)
})
.catch(handleErrors)
}
Intentionally hobbling type inference seems unfortunate, but if I wanted to, here's how I might go about doing it:
function onSubmit<
Result = [Error, "Please specify the Result type parameter"],
Variables = [Error, "Please specify the Variables type parameter"],
R extends Result = Result,
V extends Variables = Variables
>({
foo,
bar
}: {
foo: R
bar: V
}) {
console.log(foo, bar)
}
The idea is to use generic type parameter defaults to give us more control over what happens to non-inferred type parameters, and to add some extra type parameters to take away type inference sites from the compiler. The types Result and Variables cannot be inferred from the passed in values of the foo and bar properties; they are not even mentioned anywhere in the type signature. That means if Result and Variables are not manually specified, they will default to that weird tuple type of the form [Error, "error message"]. That weird tuple type is a workaround for the lack of an "invalid" type in TypeScript. I could have chosen never instead, but then the error message might be more cryptic.
Anyway foo will infer R, and bar will infer V, which must be assignable to Result and Variables respectively. What this means is that if you don't specify Result and Variables, they will default to some error type, and then foo and bar will be constrained to that error type, and you will get an error:
onSubmit({ foo: 'foo', bar: 'bar' }) // error
// ~~~ ~~~
// 'string' not assignable to 'Error, "Please specify the Result type parameter"'
// 'string' not assignable to 'Error, "Please specify the Variables type parameter"'
If you do specify Result and Variables manually, then R and V will be constrained to them (and will default to them), so should work the same as your previous version:
onSubmit<string>({ foo: 'foo', bar: 'bar' }) // error
// ~~~
// 'string' not assignable to 'Error, "Please specify the Variables type parameter"'
onSubmit<number, number>({ foo: 3, bar: 4 }) // okay
onSubmit<number, number>({ foo: 'foo', bar: 'bar' }) // error
// ~~~ ~~~
// 'string' not assignable to 'number' x2
It's ugly, because the language doesn't want you to do this... type inference is usually a good thing. But this is what you asked for, so... yay? Hope that helps. Good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With