graphql-typed-document-node icon indicating copy to clipboard operation
graphql-typed-document-node copied to clipboard

fix: update variable type variance to bivariant

Open sunnhyn opened this issue 1 year ago • 1 comments
trafficstars

Summary

I've updated the implementation of the __apiType method to shorthand method definition. This modification enables bivariance in the variable type.

Details

Let's consider a function that takes a specific TypedDocumentNode with certain requirements. Assume this function, queryAnimal, receives a Graphql query with AnimalQueryResult as the return type and AnimalQueryVariable as the argument type.

Suppose dogDocumentNode has a narrower scope than AnimalQueryResult for the Return Type ({ name: string; id: string; age: number }) and a narrower scope than AnimalQueryVariable for the Variable Type ({ id: string }). As a result, it should be usable as an argument for queryAnimal. However, due to the Variable Type of TypedDocumentNode being a function argument, TypeScript conducts type checking contravariantly for the VariableType, making it ineligible as an argument.

To create a bivariant function argument in TypeScript, the method needs to be defined as a Shorthand Definition.


type ResultOf<T> = T extends TypedDocumentNode<infer R, any> ? R : never;
type VariablesOf<T> = T extends TypedDocumentNode<any, infer V> ? V : never;

// type TypedDocumentNode<R, V> = { __apiType?: (variables: V) => R }; // AS-IS
type TypedDocumentNode<R, V> = { __apiType?(variables: V): R }; // TO-BE

type AnimalQueryResult = Record<
  string,
  { name: string; id: string; age: number; [key: string]: any }
>;
type AnimalQueryVariable = Record<string, any>;

function queryAnimal<
  DN extends TypedDocumentNode<R, V>,
  R extends AnimalQueryResult,
  V extends AnimalQueryVariable
>(query: DN, variable: VariablesOf<DN>): ResultOf<DN> {
  return "" as any;
}

const dogDocumentNode = "" as TypedDocumentNode<
  { dogQuery: { name: string; id: string; age: number } },
  { id: string }
>;
const dog = queryAnimal(dogDocumentNode, { id: "5172" });

const catDocumentNode = "" as TypedDocumentNode<
  { catQuery: { name: string; id: string } },
  { id: string }
>;
const cat = queryAnimal(catDocumentNode, { id: "5713" });

References

TypeScript FAQ: Why are function parameters bivariant? Typescript Repo: Strict function types #18654

sunnhyn avatar Jan 05 '24 10:01 sunnhyn

⚠️ No Changeset found

Latest commit: ab9e8c3eaa0b7b1ef258c50563877662ad184445

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

changeset-bot[bot] avatar Jan 05 '24 10:01 changeset-bot[bot]