You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: ensure variance of types matches how values are used
The issue with specifying them as separate properties is that you get the wrong variance.
Consider the case of:
```ts
declare function runQueryA(q: TypedDocumentNode<{output: string}, {input: string | null}>): void;
// valid
declare const optionalInputRequiredOutput: TypedDocumentNode<{output: string}, {input: string | null}>;
runQueryA(optionalInputRequiredOutput);
// invalid: query might return {output: null} but runQueryA expects to get {output: string}
declare const optionalInputOptionalOutput: TypedDocumentNode<{output: string | null}, {input: string | null}>;
runQueryA(optionalInputOptionalOutput);
// invalid: runQueryA might pass {input: null} but query expects {input: string}
declare const requiredInputRequiredOutput: TypedDocumentNode<{output: string}, {input: string}>;
runQueryA(requiredInputRequiredOutput);
// invalid: runQueryA might pass {input: null} but query expects {input: string} AND
// query might return {output: null} but runQueryA expects to get {output: string}
declare const requiredInputOptionalOutput: TypedDocumentNode<{output: string | null}, {input: string}>;
runQueryA(requiredInputOptionalOutput);
```
Because for the purposes of type checking, TypeScript assumes you will only read from queries (all properties are treated as "Covariant", it will correctly catch the inaccuracies in the Result type, but not in the Variables type. The purpose of specifying it as a function, is that it tells TypeScript that the Variables will be used as input (they are "contravariant") and the results will be read from (they are "covariant").
To see this variance in action, consider the following example, using the same queries as above, but a different runQuery definition that is more tollerant in both the input and output parameters:
```ts
declare function runQueryB(q: TypedDocumentNode<{output: string | null}, {input: string}>): void;
// still valid: We still accept {output: string} as a valid result.
// We're now passing in {input: string} which is still assignable to {input: string | null}
runQueryB(optionalInputRequiredOutput);
// valid: we now accept {output: null} as a valid Result
runQueryB(optionalInputOptionalOutput);
// valid: we now only pass {input: string} to the query
runQueryB(requiredInputRequiredOutput);
// valid: we now accept {output: null} as a valid Result AND
// we now only pass {input: string} to the query
runQueryA(requiredInputOptionalOutput);
```
0 commit comments