graphql-zeus
graphql-zeus copied to clipboard
Mutation Type Saftey
ran into an odd problem that im actively debugging
If i use
Chain(`${this.config.endpoint}/v1/graphql`, {
headers: {
"Content-Type": "application/json",
"x-hasura-admin-secret": this.config.secret,
},
})("mutation", {
scalars,
})({
insert_matches_one: [
{
object: {
bad1: 123,
}
},
{
id: true,
},
],
});
i get the proper type error
rc/hasura/hasura.service.ts:57:15 - error TS2353: Object literal may only specify known properties, and 'bad1' does not exist in type '{ created_at?: unknown; e_match_status?: { data: { description?: string | Variable<any, string>; matches?: { data: Variable<any, string> | ...[]; on_conflict?: { constraint: matches_constraint | Variable<...>; update_columns: Variable<...> | matches_update_column[]; where?: { ...; } | Variable<...>; } | Variable<......'.
57 bad1: 123,
~~~~
generated/zeus/index.ts:5997:2
5997 object: ValueTypes["matches_insert_input"] | Variable<any, string>, /** upsert condition */
~~~~~~
The expected type comes from property 'object' which is declared here on type '{ object: { created_at?: unknown; e_match_status?: { data: { description?: string | Variable<any, string>; matches?: { data: Variable<any, string> | ...[]; on_conflict?: { ...; } | Variable<...>; } | Variable<...>; value?: string | Variable<...>; } | Variable<...>; on_conflict?: { ...; } | Variable<...>; } | Variabl...'
but if i add a single valid item , the error goes away
Chain(`${this.config.endpoint}/v1/graphql`, {
headers: {
"Content-Type": "application/json",
"x-hasura-admin-secret": this.config.secret,
},
})("mutation", {
scalars,
})({
insert_matches_one: [
{
object: {
bad1: 123,
server_id: 123,
}
},
{
id: true,
},
],
});
```
```
[2:43:00 AM] Found 0 errors. Watching for file changes.
```
@lukepolo
I made a PR that fixes this! It was because the input type was extends-ed in the Chain().
For now, I use this script to replace the typing of input in Chain()
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
// take user command line argument
const path = process.argv[2];
const generatedIndexPath = join(process.cwd(), path, 'zeus', 'index.ts');
try {
const content = readFileSync(generatedIndexPath, 'utf-8');
const cleanedContent = content.replace(
'o: Z &',
'o: typeof Ops[O] |'
);
writeFileSync(generatedIndexPath, cleanedContent);
console.log('Successfully replaced Z & with Ops[O] |');
} catch (error) {
console.error('Error cleaning up Zeus generated code:', error);
process.exit(1);
}
@soya-miyoshi Really that PR breaks almost all of the features. To fully fix it you need to dive deeper
@aexol Thank you very much for your feedback. I truly appreciate the time you took to review my PR and your ongoing work building and maintaining this library. It has been invaluable to me.
In my use cases, both the old and new Chain functions appear to return the same type.
However, it’s possible that my change could break type inference for the IsPayload input in certain edge cases.
I apologize for any confusion this may have caused. In my own usage, the type inference behaves exactly as I intended.
The script I provided above may indeed help users who invoke Chain() in the manner shown.
That said, because there could be scenarios—such as when the inferred type Z differs in the Thunder() function—where inference does not work as expected, I should have added a cautionary note advising users to apply the script with care.
Below is an example of how I currently use the Chain() function; in my context, it also returns the same type.
I will update whenever I found a misbehavior that my new change might cause. Again, really sorry for the confusion caused.
// please pretend that Chain2 is that one one I patched, Chain is the one without the change
const res2 = await Chain2("", {})('query')({
[Q]: [
{
where: {
username: { _eq: username },
},
},
{
id: true,
}
],
});
const res = await Chain("",{})('query')({ // Chain before my patch
[Q]: [
{
where: {
username: { _eq: username },
},
},
{
id: true,
}
],
});
type b = typeof res2 extends typeof res ? true : false; // true
type c = typeof res extends typeof res2 ? true : false; // true
// res: {
// auth_users: {
// id: unknown;
// }[];
//}