Javascript Runtime Limitations
Hosanna brings JavaScript and TypeScript to Roku, but due to the underlying BrightScript environment, some features are not fully supported or may behave differently. Below is a summary of current limitations and recommended workarounds.
Unsupported Features
Call Expressions
parseInt
does not support hexadecimal (0x10
) or binary (0b10
) string input. UseparseInt("10", 16)
orparseInt("10", 2)
instead.isFinite
returnstrue
for numbers,false
otherwise.isNaN
returnstrue
for numbers,false
otherwise.parseFloat
may not support scientific notation—please verify for your use case.
Identifier Expressions
Infinity
andNaN
are not supported.
Math Functions
- Some math functions may behave differently from JavaScript, especially for edge cases and less common functions.
Math.acos(-1)
does not work.Math.fround
does not work correctly.Math.atan2
truncates results to 4 decimal places.
Console
Only the following methods are implemented:
log
error
warn
info
debug
trace
Logging arrays: You cannot log an inline-declared array after a string due to BrightScript limitations:
// Does not work
console.log("text", ["inline array"]);
// Works
console.log(["inline array"], "text");
Object Functions
Object.keys / Object.values / Object.entries
- These methods do not support strings, the prototype chain, or arrays.
Other Object Functions
- Some object utility functions are only partially supported and may not behave exactly as in JavaScript.
Array
Instance Methods
Array.prototype.push
does not return the new length for a simple push (for performance reasons); it is slow for variadic arguments but does return the length in that case.Array.prototype.unshift
only supports adding a single item and does not return the new length.Array.prototype.concat
does not flatten nested arrays:[1,2,3].concat([4, 5], [6, 7]) // => [1, 2, 3, [4, 5], [6, 7]]
Array.prototype.join
is currently slow; future versions may optimize this for string arrays.
String
String.prototype.codePointOf
andnormalize
are not implemented.
Closures
Supported:
- Updating instance variables, getters/setters, and module variables.
Not Supported:
- Updating closure-captured variables that are not instance or module variables. To work around this, wrap the variable in an object and capture that object:
const foo = () => {
let captured = { value: 0 };
return () => {
captured.value++;
console.log(captured.value);
};
}; - Immediately Invoked Function Expressions (IIFE) are not supported. Instead, declare a function and call it immediately:
const foo = () => { }; foo();
- Capturing getters and setters is not supported.
Function Pointers
Not Supported:
- Passing a function pointer to a global function that is not in a module.
- Assigning a function to an anonymous object (declare locally, assign, and bind instead).
Supported:
- Passing function pointers to global functions declared in a module, class instance methods, or locally declared functions.
- Binding function pointers as expected.
Operators
Spread
- You cannot spread strings into objects, or spread anything with a type declaration.
||
for Assignment
- The
||
operator is not always reliable for assignment and can be slow; prefer using??
instead:let a = b ?? 4;
- Chained assignments using
||
may not transpile correctly; split them into multiple lines if you encounter issues.
Modules and Imports
Not Supported:
- Importing non-TypeScript files (such as text, XML, or JSON).
- Exporting something before importing it.
- Using
export default
.
Example:
// BAD
export { DebugMenuViewStruct, DebugMenu };
import { DebugMenuViewStruct, DebugMenu } from './DebugMenu-generated-struct';
// GOOD
import { DebugMenuViewStruct, DebugMenu } from './DebugMenu-generated-struct';
export { DebugMenuViewStruct, DebugMenu };
Equivalence
- Deep equality is limited; object equivalence only works if objects implement
IIdentifiable
(using the_hid
field). - Enums are supported only if all enum values are of the same type.
- If the type cannot be properly inferred, you will receive a transpiler warning. For better performance, cast unknown types before checking equality.
Example:
// If `field` is a union or unknown type, it may not be properly inferred, and will use slow equality checks.
if (field === 'translation') { ... }
// This will use fast equality checks, as it casts `field` to a string.
if ((field as string) === 'translation') { ... }
Types, Booleans, and Truthiness
- Union types with booleans will always resolve to boolean.
- Casting arrays to booleans is not supported.
Example:
const source: any[] = ['yes', 0, null];
const v: boolean[] = source; // Not supported
Type Philosophy: Types are enforced at declaration, assignment, and invocation. Variables are marshalled to the expected type at the point of use.
Framework Limitations
- Importing JSON files is not supported, as it can impact performance. Instead, use code such as
const debugFlags = JSON.parse(ReadAsciiFile('pkg:/assets/meta/debug-flags.dev.json'))
.
Class
Not Supported:
- Declaring classes inside functions.
- Having two classes with the same name anywhere in the codebase.
Date
The native JavaScript Date
object is not supported in Hosanna.
Instead, you should use the drop-in replacement HsDate
, which provides similar functionality and is designed to work reliably in the Roku/BrightScript environment.
Intervals and Timeouts
setInterval
and setTimeout
are supported in Hosanna. However, you cannot use Node.js-style return types for their handles.
The recommended way to declare variables for interval or timeout handles is:
private debounce?: ReturnType<typeof setTimeout>;
This ensures compatibility with the Hosanna runtime and avoids type errors.