dotansimha / graphql-code-generator-community

MIT License
118 stars 155 forks source link

Incomplete type annotation for apollo's KeySpecifier type #30

Open ruiconti opened 1 year ago

ruiconti commented 1 year ago

Which packages are impacted by your issue?

@graphql-codegen/typescript-apollo-client-helpers

Describe the bug

There is a disconnect i.e. a missing type between the type generated for <T>KeySpecifier and apollo's actual interface.

Your Example Website or App

https://codesandbox.io/s/bug-gqlgen-apollo-keyspecifier-type-mismatch-wgjgkj?file=/repro.ts

Steps to Reproduce the Bug or Issue

For example, considering the schema

type T {
   Foo: String
   Bar: String
}

After running the typescript-apollo-client-helpers, plugin, it would generate the following type policy for T:

export type TKeySpecifier = ('Foo' | 'Bar' | TKeySpecifier)[];
export type TFieldPolicy = { /* irrelevant to this issue */ };

Expected behavior

Considering the actual type shape for the keyFields property —which includes a function to generate a type's keyField— I would expect the following type annotation:

export type TKeySpecifier = ('Foo' | 'Bar' | TKeySpecifier | (o: Readonly<T>, context: KeyFieldsContext) => string)[];

Screenshots or Videos

No response

Platform

Codegen Config File

No response

Additional context

No response

soulfresh commented 4 hours ago

I used the following Yarn patch to improve the typing around this. My patch could be improved by finding a better type for the first parameter to TKeySpecifier function.

diff --git a/cjs/index.js b/cjs/index.js
index f009aab1baac950f33fe342dd11fe640d9e95a01..4e2ee3591aeb839df8cf239c54b5cb8ba784114c 100644
--- a/cjs/index.js
+++ b/cjs/index.js
@@ -23,6 +23,7 @@ function generateTypePoliciesSignature(schema, config) {
             const fieldsNames = Object.keys(type.getFields()).filter(f => !f.startsWith('__'));
             const keySpecifierVarName = `${typeName}KeySpecifier`;
             const fieldPolicyVarName = `${typeName}FieldPolicy`;
+            const fieldType = `{ [P in keyof ${fieldPolicyVarName}]: any }`;
             perTypePolicies.push(`export type ${keySpecifierVarName} = (${fieldsNames
                 .map(f => `'${f}'`)
                 .join(' | ')} | ${keySpecifierVarName})[];`);
@@ -34,7 +35,7 @@ ${fieldsNames
             return {
                 ...prev,
                 [typeName]: `Omit<TypePolicy, "fields" | "keyFields"> & {
-\t\tkeyFields${config.requireKeyFields ? '' : '?'}: false | ${keySpecifierVarName} | (() => undefined | ${keySpecifierVarName}),
+\t\tkeyFields${config.requireKeyFields ? '' : '?'}: false | ${keySpecifierVarName} | ((o: Readonly<${fieldType}>, context: KeyFieldsContext) => undefined | ${keySpecifierVarName}),
 \t\tfields?: ${fieldPolicyVarName},
 \t}`,
             };
@@ -55,6 +56,7 @@ ${fieldsNames
     return {
         prepend: [
             `import ${config.useTypeImports ? 'type ' : ''}{ FieldPolicy, FieldReadFunction, TypePolicies, TypePolicy } from '@apollo/client/cache';`,
+            `import { KeyFieldsContext } from '@apollo/client/cache/inmemory/policies'`,
         ],
         content: [...perTypePolicies, rootContent].join('\n'),
     };
diff --git a/esm/index.js b/esm/index.js
index 574a6119c6c10e6b840ac9ec77a18bd577225f94..a21b5858177daae49f5a5143617f6bbec6cd3738 100644
--- a/esm/index.js
+++ b/esm/index.js
@@ -19,6 +19,7 @@ function generateTypePoliciesSignature(schema, config) {
             const fieldsNames = Object.keys(type.getFields()).filter(f => !f.startsWith('__'));
             const keySpecifierVarName = `${typeName}KeySpecifier`;
             const fieldPolicyVarName = `${typeName}FieldPolicy`;
+            const fieldType = `{ [P in keyof ${fieldPolicyVarName}]: any }`;
             perTypePolicies.push(`export type ${keySpecifierVarName} = (${fieldsNames
                 .map(f => `'${f}'`)
                 .join(' | ')} | ${keySpecifierVarName})[];`);
@@ -30,7 +31,7 @@ ${fieldsNames
             return {
                 ...prev,
                 [typeName]: `Omit<TypePolicy, "fields" | "keyFields"> & {
-\t\tkeyFields${config.requireKeyFields ? '' : '?'}: false | ${keySpecifierVarName} | (() => undefined | ${keySpecifierVarName}),
+\t\tkeyFields${config.requireKeyFields ? '' : '?'}: false | ${keySpecifierVarName} | ((o: Readonly<${fieldType}>, context: KeyFieldsContext) => undefined | ${keySpecifierVarName}),
 \t\tfields?: ${fieldPolicyVarName},
 \t}`,
             };
@@ -51,6 +52,7 @@ ${fieldsNames
     return {
         prepend: [
             `import ${config.useTypeImports ? 'type ' : ''}{ FieldPolicy, FieldReadFunction, TypePolicies, TypePolicy } from '@apollo/client/cache';`,
+            `import { KeyFieldsContext } from '@apollo/client/cache/inmemory/policies'`,
         ],
         content: [...perTypePolicies, rootContent].join('\n'),
     };