Open masaeedu opened 1 year ago
Came here to report that the out
keyword is optional and it also happens here:
type F<out A, out B> = <X>(v: X extends A ? X : never) => B
const a: F<never, never> = v => v
const b: F<number, never> = a
const result: never = b(42) // whoops
@nightlyherb Hmm, not sure I'm following. The out
keyword in the repros given is material, the point is to demonstrate that the typechecker agrees with the incorrect/unsound variance annotation given.
The snippet you gave also contains an out
annotation, is that a typo?
I just tried to say that all the examples you gave and the example I gave exhibits buggy behavior with or without variance annotations.
The buggy behavior without variance annotations might be a separate issue, but I thought it was worth a mention since it's so similar and it might be a related issue.
The buggy behavior without variance annotations might be a separate issue, but I thought it was worth a mention since it's so similar and it might be a related issue.
@nightlyherb You're right that the issues are related, in fact I think they're mostly indistinguishable. The buggy behavior we're talking about is the ability to assign const b: ... = a
, which is accepted due to the (incorrectly) assumed covariance of a particular parameter. If the typechecker correctly determined that the relevant type parameters were contravariant instead, both my incorrect out
annotations and the assignment would be rejected.
A type parameter appearing only in a constraint is kinda sus, and generally you would want to write X & A
in a usage position wherever you write <X extends A>(..
. But that doesn't even work here. I think the variance is just not being measured correctly. Examples:
// Incorrect: Fails to error
type F1<out A> = <X>(v: X & A) => unknown;
// Correctly errors
type F2<out A> = <X>(v1: X, v2: A) => unknown;
// Correctly errors
type F3<out A> = <X>(v2: A) => X;
I have encountered a situation where this sus usage could be useful.
// F seems to behave like a generic type contravariant over Xi,
// but typescript doesn't error on either in Xi or out Xi,
// so I cannot use this generic type.
type F<Xi> = <X extends Xi>(x: X) => X;
// I can instantiate this generic type manually
// to observe the contravariant behavior.
// number is a subtype of unknown and
// F<unknown> seems to be a subtype of F<number>
declare const funknown = <X extends unknown>(x: X) => X;
declare const fnumber = <X extends number>(x: X) => X;
const funknown2: typeof funknown = fnumber; // error
const fnumber2: typeof fnumber = funknown; // no error
// this does seem natural because `funknown` is assignable
// to a superset of types assignable from `fnumber`
May I ask, is my observation correct? i.e. is F contravariant over Xi in current typescript?
Bug Report
š Search Terms
variance, methods, generics
NB: I did not search very exhaustively, but I did minimize the examples, so should be pretty easy to review and close as dupe or "by design" or whatever.
š Version & Regression Information
strictFunctionTypes
āÆ Playground Link
Variance checking does not interact correctly with
extends
:https://www.typescriptlang.org/play?#code/C4TwDgpgBAYgPAewK7CgQQDRWagQgPigF4o4ANKCAD2AgDsATAZ3WPXwAoA3ALijICUxQrgBQogMYI6TVAEM+8OhC4QATlmWq1hEl2FQuk6bKgAjRXDpIAtmfWaV63VDnGZqNRCZIANsD4tdTYzDgAWACYhAHpoqAB3AAsEBDAmIA
Variance checking does not interact correctly with "records with methods" types:
https://www.typescriptlang.org/play?#code/C4TwDgpgBAGgPAewK7CgZQHxQLxQN4BQUUAHgBQDOAXOgJQ0B2EAbhAE4EC+BBAxggwqoAhjXhNWbLLjykaFHFgXd+g1ACMxcJAwDWDBAHcG0qML4ChUQwAsECMNSgT2OKOoB05ACwAmWgRAA
š» Code
Variance checking does not interact correctly with
extends
:Variance checking does not interact correctly with "records with methods" types:
š Actual behavior
In both cases the typechecker allows me to inhabit
never
.š Expected behavior
The typechecker should reject my incorrect variance annotations (
F<out A, ...>
andX<out S>
) and not allow me to inhabitnever
.Incidentally this came to light out of some discussion in the "forall for non-functions" issue here: https://github.com/microsoft/TypeScript/issues/17574#issuecomment-1465094919, but these things don't really seem materially related.
Related to #48240.