Skip to content

Handle Java generics cases better, emit more strict TS generics #49

@niloc132

Description

@niloc132

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.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions