-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Java arrays are covariant/contravarient, but Java generics are not. This plugs this particular bug:
Number[] numbers = new BigInteger[] { new BigInteger(1), null };
numbers[1] = (Integer) 1;The cast in the last line isn't necessary, but just to illustrate the point - we're asking Java to put a Integer in an array only meant for BigIntegers. In contrast, consider how a list would look if you tried to make it more generic:
List<? extends Number> numbers = Array.asList(new BigInteger(1));
numbers.add((Integer) 1);//compile error!This time the compiler catches us, and tells us we're wrong, because while BigInteger and Integer both satisfy ? extends Number, by the time we get to the second line, we don't know which subtype of Number actually should match the wildcard - that's what the wildcard is supposed to mean.
This also won't work, and will fail on the first line, no need to even try the second:
List<Number> numbers = Arrays.asList(new BigInteger(1));On the other hand, over in TypeScript, generics are covariant/contravarient, you can "upcast" and illegally widen a type, such that the original producer of this object can no longer actually use it in a type safe way. This is super easy to demonstrate:
https://www.typescriptlang.org/play?#code/MYGwhgzhAEBiD29oG8CwAoaXoDswFsBTALggBcAnASxwHMBuDbaYeHcigV2DPgoAo8RUpRq0AlCiYBIZmQAWVCADohhaAF5cBQo0zYAvhiPpQkGACEwFaIQAeZQjgAmMBEjTppYWiRyd8ACNCCj1pVnZKbl4BNRFqOgAaaB8-AOCKSU9paQhOAAcQwR1xMOkFJWVUzRTfMJMTMyg4RABhNjIwGhCpLwB6PpYwHBYOrpH3FJdoKwoMbwoKMABPYgBBRZWAHncAPnmIjmi+fiz58sUVayXlmoBtAF16g-lCYABrU+IAN3gqZ16OQqV02y2UADM+ABRMDAeT8eCBABWml2gJyh3gIEIyhA8FoCORqhKZWkA0x2Nx+MJSKqvnEA3kYG+6l40Ec5GgVHBM2s7KQEEI6m50AA7upgMNoM4kMDlPLzgZxPMTMYME1LNZ2jhOt0bPZHC43G0xnrAQMhiM2CBbhFdSNZskcIQWTZ3PNrit1qCtrN9l5DlEeCczl5cgUiqVznLPbctI9nmG4W9PuIfn8AdkLpVYxDobD4YiURo0VnwmwIFicXiCUXiUQo2Hy+wq1Ta0TUgzBvhOJz7EoyMlApwyGKqCAQGLqI5oAAiTguQjg7rOWfJF1OdnyeCcWjyWoS4Y4eCj4LQBfOJcrgCEiuVXgaGAw4IXPCobBSzmc-GAxHc2vtEJQ3CKpQWUfJe3hZ1RRaeB+AAckgZxwXg8R70aCtRztcYei0aDeQoACcIERswC-H9TWdTJ1UokJlGTD5TiAA
Based on https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#optional-variance-annotations-for-type-parameters, I believe that the default generics emitted by this tool should translate <SomeType> in Java into <in out SomeType> in TS, to make it invariant. In turn, <? extends SomeType> should be <out SomeType>, and <? super SomeType> should be <in SomeType>. Following this guidance, it should never be possible to emit <SomeType> in any TS from Java.
As a short term, test fix, we can probably just transition from <SomeType> to <in out SomeType>, to make all generated generics invariant, and then work our way back to closer semantics for Java PECS.