NativeScript 9.1 Released → V8 14.9, Vite 8 HMR, built for rapid agentic visual iteration
Dig in

A compiled release runs your TypeScript as Swift and Kotlin, against a runtime that reproduces JavaScript's semantics. This page lists where that stops: what the build refuses, and the differences known today between a compiled release and the JavaScript release of the same app. Each entry names the platform it affects. Check every compiled release against its JavaScript release before you ship it (Checking a compiled release).

What the build refuses ​

Before anything is compiled, the compiler checks the app (and the plugin code the app reaches) and stops on each of these with the file, the line and what to write instead. All of them are reported at once.

ConstructWhyInstead
new Proxy(…)No traps without a JavaScript engineA class with explicit getters and setters, or a Map for dynamic keys
Reflect.*Not provided by the runtimeThe operation itself: obj[key], key in obj, Object.keys(obj), new Cls(...args)
eval(…), new Function(…)There is no engine to evaluate codeA lookup of known functions; or keep this app on the JavaScript release
FinalizationRegistryFinalizers do not runRelease the resource explicitly (dispose(), unloaded, disposeNativeView)
SharedArrayBuffer, AtomicsNo shared memory between workersAn ArrayBuffer, with copies posted between workers
with (obj) { … }Name the object explicitly
import(expr), require(expr) with a computed specifierEvery module is linked when the app is builtImport each module by a literal path, and choose among those at run time
arguments.callee, __proto__A named function; Object.getPrototypeOf
A bundler constant nothing defines (declare const __API_URL__, process.env.X)It would read as undefined at run timeDefine it in the bundler config: Vite's define and webpack's DefinePlugin values are read and compiled in

The build also stops, before compiling, when:

  • The app's @nativescript/core is not the core the compiler's kit was generated from. The kit is core's own code, compiled; the native widgets it drives come from your installed core, so the two must be the same release. Install the core version the message names, or the @nativescript/compiler released with your core. release: { allowCoreMismatch: true } builds anyway, with a warning.
  • A framework package is outside the versions its support is tested with (table below). release: { allowUntestedFrameworks: true } builds anyway, with a warning.
  • The toolchain is missing or too old: Xcode 26 or newer for iOS; JDK 17 or newer and the Android SDK for Android.

Other unsupported language and framework constructs are refused by the compiler as it meets them, with the file and line; see What stops a build.

Tested framework versions ​

FrameworkPackages and versions
Vuenativescript-vue 3.1 and later 3.x
Angular@nativescript/angular and @angular/core 22.x
Reactreact-nativescript 5.x with react 18.2 or later 18.x
Solid@nativescript-community/solid-js and solid-js 2.0 (from 2.0.0-rc.0)
Sveltesvelte-native 1.x with svelte 4.2+, or svelte-native 5 (from 5.0.0-alpha.0) with svelte 5.50+
Octane@nativescript-community/octane 0.2.4 and later 0.2.x

Framework features each front end does not support yet stop the build with a message. The main ones:

  • Angular: pipes other than async; a pipe inside *ngFor/@for; OnPush under zone.js; more than one @Component in a file. Templates are parsed with the compiler's own @angular/compiler 22.
  • React: a component body may hold function declarations, variable declarations and the return; a bare statement such as useEffect(() => …, []) on its own line is refused. JSX spread attributes are refused.
  • Vue: <style module>, and Options API keys beyond the common ones.

Known differences at run time ​

These compile and run, and behave differently from the JavaScript release. They are being fixed; until then, avoid relying on them.

Values and types ​

  • null and undefined are one value in a typed optional (both platforms). With b?: string | null, an explicit null reads as undefined, disappears from JSON.stringify, and a destructuring default applies to it. Code that tells === null from === undefined on such a field takes the wrong branch.
  • Fields of an Error subclass read through any or unknown are undefined (both): catch (e: any) { e.code }. On Android, also after instanceof.
  • A value of the wrong type in a typed slot can stop the app instead of throwing a TypeError (iOS): an untyped value (any, parsed JSON) assigned where a class or interface is declared, a downcast to the wrong subclass, a definitely-assigned field (x!: T) read before it is set, an out-of-union value in an exhaustive switch. Validate parsed data before typing it.
  • Number to integer conversions of NaN, Infinity or very large values can stop the app (iOS), where JavaScript gives 0 or wraps.
  • valueOf() is never called: arithmetic on objects concatenates or gives NaN (both).
  • typeof SomeClass is "object" (both), and typeof new String('x') is "string".
  • Subclass fields are initialized before the base constructor runs (both), so a base constructor that calls an overridden method sees the subclass's fields already set.
  • var in a for loop gets a new binding per iteration (both), as let does.

Strings and regular expressions ​

  • \d, \w, \b and . follow Unicode rules (iOS): \d matches Arabic-Indic digits, \w matches accented letters.
  • A capture group that did not take part reads '', not undefined (iOS).
  • replace with a string pattern ignores $&, $`, $' and $$ (iOS).
  • Cutting a string between the two halves of a surrogate pair gives U+FFFD (iOS), so an emoji split and rejoined is corrupted.
  • str[i] past the end is '', not undefined (both).
  • === on strings compares by Unicode canonical equivalence (iOS): 'é' precomposed equals 'e' + accent, while Map and Set keys keep them apart.
  • localeCompare ignores its locale and options (both). lastIndexOf(x, fromIndex) ignores fromIndex.

Arrays, dates, JSON ​

  • Holes in a number[] read as NaN (iOS). flat(depth) ignores the depth (iOS).
  • Date.parse accepts out-of-range ISO fields and rolls them over (iOS).
  • JSON.parse ignores a reviver (both).

Functions, listeners and memory ​

  • A function read twice is not === to itself (iOS), so off(handler) and removeEventListener do not remove a listener, and the same handler added twice runs twice. Keep the subscription handle where an API returns one.
  • Closures stored on an object, and parent and child views, hold each other strongly (iOS): a page navigated away from may stay in memory.
  • WeakMap and WeakSet lack ephemeron semantics (both): a value that reaches its key keeps both alive.
  • WeakRef timing differs: on iOS a target can be freed within the job that created it; on Android a WeakRef is a soft reference and survives ordinary collections.
  • Script entered from a native background thread is not serialized with the main thread (both).

Errors and crashes ​

  • Uncaught errors are printed, and Application.on('uncaughtError') does not fire (both); the uncaughtErrorPolicy setting is not read.
  • error.stack lists the native frames the error was made in, not JavaScript frames: on iOS symbol (App+0xoffset), which the archive's dSYM resolves to your TypeScript lines; on Android class.method(File.kt:line), which ns-native-retrace maps to your TypeScript lines.
  • Deep recursion crashes (iOS) instead of throwing a RangeError.

Android release builds (R8) ​

Members reached by name at run time (a method called on an any value, typeof x.getFoo, 'getFoo' in x) are kept from R8's renaming by a scan of the compiled code. A name computed at run time (x[name]() with name built from strings) is not found by the scan; keep such members with a ProGuard rule in App_Resources/Android/app.gradle, and check the minified release on a device.

By default every class and member of the kit is kept whole, which leaves most of the kit's code to R8 untouched. android: { release: { narrowKitKeeps: true } } keeps only what the kit's reflection needs (class names, constructors, the names of the members that remain) and lets R8 remove the rest: on Recipes (Vue) the kit's dex goes from 3.3 to 2.0 MB and the APK from 2.44 to 1.92 MB, with both screens matching the JavaScript release. It is opt-in until it has been verified on larger apps; under it, a kit member reached only through a computed name is removed.