Why
On the JavaScript target, String.charCodeAt(index) correctly returns Null<Int> because an arbitrary index may be out of range.
haxe.io.Bytes.toHex, however, only reads the fixed string "0123456789abcdef" at indices produced by 0...str.length, so every value inserted into its lookup table is an Int:
var chars = [];
var str = "0123456789abcdef";
for (i in 0...str.length)
chars.push(str.charCodeAt(i));
Because chars is unannotated, the JS stdlib currently types it as Array<Null<Int>> even though the loop establishes an integer-only table.
This becomes observable to custom typed JavaScript generators. StringBuf.addChar(c:Int) and String.fromCharCode(code:Int) are inline, so their Int formals no longer exist in the final typed tree. A generator sees the nullable table read inside the surviving raw JavaScript expression and cannot soundly recover the erased Int destination.
For example, a strict TypeScript surface can consequently reach the equivalent of:
const chars: Array<number | null> = [];
String.fromCodePoint(chars[index] ?? null);
TypeScript correctly rejects number | null as the argument of String.fromCodePoint(number).
Proposed fix
State the local invariant where the lookup table is built:
-var chars = [];
+var chars:Array<Int> = [];
This preserves an ordinary typed Null<Int> -> Int boundary at Array<Int>.push. A typed generator can then handle that exact Haxe-accepted boundary without inferring a type from raw JavaScript template text.
Compatibility
The annotation is type-only. The normal Haxe JavaScript output and runtime behavior should remain unchanged.
Prepared by the GameCarry agent.
Why
On the JavaScript target,
String.charCodeAt(index)correctly returnsNull<Int>because an arbitrary index may be out of range.haxe.io.Bytes.toHex, however, only reads the fixed string"0123456789abcdef"at indices produced by0...str.length, so every value inserted into its lookup table is anInt:Because
charsis unannotated, the JS stdlib currently types it asArray<Null<Int>>even though the loop establishes an integer-only table.This becomes observable to custom typed JavaScript generators.
StringBuf.addChar(c:Int)andString.fromCharCode(code:Int)are inline, so theirIntformals no longer exist in the final typed tree. A generator sees the nullable table read inside the surviving raw JavaScript expression and cannot soundly recover the erasedIntdestination.For example, a strict TypeScript surface can consequently reach the equivalent of:
TypeScript correctly rejects
number | nullas the argument ofString.fromCodePoint(number).Proposed fix
State the local invariant where the lookup table is built:
This preserves an ordinary typed
Null<Int> -> Intboundary atArray<Int>.push. A typed generator can then handle that exact Haxe-accepted boundary without inferring a type from raw JavaScript template text.Compatibility
The annotation is type-only. The normal Haxe JavaScript output and runtime behavior should remain unchanged.
Prepared by the GameCarry agent.