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 what is checked, what the build refuses, and where a compiled release still behaves differently from the JavaScript release. The status is as of 2026-10-15.

Frameworks and apps ​

Each app is compared with its own JavaScript release build, screenshot by screenshot.

AppiOSAndroid
XML + TypeScript galleryIdentical (0 px)Identical (0 px, minified)
Vue gallery189 of 191 shots identical; a text caret (timing) and one HtmlView differIdentical (0 px, minified)
Angular galleryIdentical (0 px)Identical (0 px, minified)
Svelte 5 galleryIdentical (0 px)Identical (0 px, minified)
OpenJS app (Angular, eight plugins)Builds and launches47 of 49 shots identical; the other two are run-to-run variance
ns-duo-guitar (Angular, WebGPU, audio worker)On the App StoreOn Google Play

React, Solid and Octane compile with the same pipeline and are supported; they are not in the comparison runs above.

Tested framework versions ​

The build stops on a framework version outside these (release.allowUntestedFrameworks: true builds anyway, with a warning).

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.x (from 2.0.0-rc.0)
Svelte@nativescript-community/svelte-native 1.x with svelte 4.2+, or 5.x (from 5.0.0-alpha.0) with svelte 5.50+
Octane@nativescript-community/octane 0.2.4 and later 0.2.x
Plain TypeScript with XMLCore's own Builder: bindings, code-behind, custom components and ListView templates

Framework features not supported yet stop the build with a message. The main ones:

  • Angular: under zone.js, a component that doesn't set changeDetection: ChangeDetectionStrategy.Default; pipes other than async; a pipe inside *ngFor/@for; more than one @Component in a file. RxJS is a subset.
  • 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>, <style src>, a prop default, and Options API keys other than name, components, props, emits, data, computed, methods, watch and setup.

Plugins and platform APIs ​

Kind of pluginIn a compiled release
TypeScript over native APIs (most plugins)Compiled from the plugin's source, as part of your app
Native code in platforms/ios / platforms/androidLinked unchanged
Installs objects into the JavaScript engine (@nativescript/canvas)Needs a native counterpart in the kit; canvas has one on both platforms
Only meaningful with a JavaScript runtime (over-the-air updates)Replace it with release.pluginReplacements

Plugin native packaging not carried yet stops the build: a prebuilt .framework or .a (an .xcframework is fine), a resource .bundle, a .podspec; on Android, jniLibs/, .so files, assets/ and cpp/ in a plugin's platforms/android, and its buildscript.gradle, rootbuildscript.gradle, include-settings.gradle or before-plugins.gradle.

The web globals the NativeScript runtime provides are there on both platforms: crypto (randomUUID, getRandomValues, and subtle with SHA digests, HMAC and RSA-OAEP), TextEncoder/TextDecoder, atob/btoa, timers, queueMicrotask and console. fetch, XMLHttpRequest, FormData, Blob and AbortController are available on iOS only.

What the build refuses ​

Before anything is compiled, the compiler checks the app and the plugin code it reaches, and reports each of these with the file, the line and what to write instead, all 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 don't 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 them 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 undefinedDefine it: Vite's define and webpack's DefinePlugin values are compiled in

The build also stops when:

  • @nativescript/core isn't the core the compiler was made from. Install the version the message names (release.allowCoreMismatch: true builds anyway, with a warning).
  • The toolchain is missing or too old: Xcode 26 for iOS; JDK 17 and the Android SDK for Android.
  • A language construct isn't supported yet, such as a class declared inside a function that reads the function's own variables, and on iOS a default clause before other cases or a return or throw in a finally block.
  • A library member isn't provided yet: Object.create, String.prototype.normalize, Intl.Collator, Intl.PluralRules, Intl.RelativeTimeFormat, a class expression (const Mixin = (B) => class extends B {}) and an array literal with holes ([1, , 3]). Intl.NumberFormat, Intl.DateTimeFormat, toLocaleString and the ES2023 array and grouping methods are provided.

A few valid constructs still fail later, in the Swift or Kotlin compile, reported at your TypeScript line. The ones known today are listed in Not fixed yet.

JavaScript coverage ​

The compiler is also run against test262, the JavaScript conformance suite. "Behaves as Node" is the share of the tests the compiler accepts whose output matches Node. "Refused" are tests stopped at build time, most of them by constructs above or by code no typed app contains. test262 exercises edge cases far more than apps do: read this as a map of where differences live, not as how much of an app works.

AreaTestsiOS behaves as NodeAndroid behaves as NodeiOS refusedAndroid refused
String91658%52%276269
Template literals8454%48%01
Array268734%27%13781338
Number29477%82%8187
Math16983%87%1111
JSON13673%76%77
Object54973%61%5685
Map16582%78%7170
Set16263%83%9494
Promise67519%55%160173
Date32764%66%8588
RegExp83056%55%41137
Destructuring65123%35%5557
Classes66620%19%10994
Getters and setters644%2%32
Closures and scope36630%42%97
Optional chaining and nullish14052%73%23
typeof and equality16147%68%34
Exceptions29545%37%1510
Total (with 5 harness tests)934250%50%24562537

test262 d53ebc8b19, compiler 2026-10-14. App-shaped code is held to a stricter bar: 271 differential cases, each run under Node and compiled, all matching except those under Not fixed yet.

Known differences at run time ​

These compile and run, and behave differently from the JavaScript release. The most likely to affect an app come first.

null and undefined are one value in a typed optional (iOS and Android) ​

Where a type admits both (name?: string | null), === null and === undefined don't tell them apart: depending on where the value is held, an explicit null reads as undefined, or as both, and undefined passed to a parameter x?: string | null can read as null. A destructuring default applies to null. Where the value is held typed, JSON.stringify leaves the key out instead of writing null.

Instead: test with == null, which is true for both. Where an API needs an explicit null in JSON, build that JSON by hand.

A value of the wrong type throws a TypeError (iOS and Android) ​

JavaScript keeps a value of the wrong type and carries on. In your app's code, a compiled release throws a TypeError you can catch: when an untyped value (any, parsed JSON) is assigned where a class, a string, a number or a boolean is declared, and on a downcast to the wrong subclass (shape as Circle holding a Square). Core's own untyped paths, such as XML attributes and bindings, convert as JavaScript does (a binding's 5 for a string is "5").

Parsed JSON typed as an interface reads a missing string field as '' and a missing number as NaN, where JavaScript has undefined.

Instead: validate parsed data before you type it, and mark fields that may be missing optional (age?: number).

A class's getters, setters and methods are not found by name (iOS) ​

In an app built with a framework, a member a class declares as a getter, a setter or a method is undefined when read through any or a computed key: (order as any).total, order[key], 'total' in order. An assignment through any doesn't run the setter. Fields are found. Android finds all of them, and so does iOS in an XML app, where core's bindings read them.

Instead: read members through a typed reference. For duck typing, test a field, or use instanceof.

Functions in arrays, Sets and Maps are not found again (iOS; Android for methods) ​

A function read twice is === to itself, so obj.off('tap', handler) works. But on iOS a function stored in an array, a Set or as a Map key isn't found by indexOf, includes, has, delete or get. On Android this applies to a method read as a value ([this.onTap].indexOf(this.onTap)).

Instead: have on() return a subscription object or an id, and remove by that.

Pages and views can stay in memory (iOS) ​

iOS frees objects by reference counting. A closure that a page's view holds and that captures the page (button.on('tap', () => this.save())) is a cycle, so a page you navigated away from can stay in memory. Core's own references don't cause this. A WeakMap value that refers to its own key keeps both alive; on Android, a WeakRef's target is held until memory runs low.

Instead: remove the listeners you add in unloaded or navigatedFrom, or reach the page through a WeakRef.

Native callbacks from other threads run on the main thread (iOS and Android) ​

A callback a native API calls on a background thread runs on the main thread. One that returns nothing runs later, as a task. One that returns a value runs while the calling thread waits, so it deadlocks if the main thread is waiting for that thread.

Instead: don't block the main thread on a native thread that calls back into your code. Do heavy work in a worker or in native code.

Regular expressions (iOS and Android) ​

On iOS, a capture group that took no part in the match reads '', not undefined (/(a)|(b)/.exec('b')[1]), and a capture inside a repeated group keeps an earlier iteration's value. On both platforms, a replace callback's group parameter declared string isn't === undefined for such a group.

Instead: test with !m[1] or m[1] || fallback, and declare replace callback groups optional ((all, x?: string) => …).

Arrays with holes (iOS and Android) ​

An array with holes (written past its end, or new Array(n) not filled) doesn't behave as in JavaScript: writing past the end of an array of objects can stop the app on iOS, and iteration and Object.keys treat holes differently.

Instead: push, or create the array at its length with values (new Array(n).fill(0)).

Missing values on iOS can stop the app ​

A member of undefined or null read, set or called, and a module constant read before its declaration runs, stop the app on iOS instead of throwing a TypeError or ReferenceError. Deep mutual recursion (two functions calling each other) also stops it; a single recursive function throws a RangeError.

Strings (iOS and Android) ​

  • str[i] past the end is undefined, but a call on it doesn't throw: str[9].toUpperCase() gives '' on iOS and 'UNDEFINED' on Android.
  • On iOS, cutting inside a surrogate pair ('a😀b'.slice(0, 2)) gives U+FFFD, and JSON.stringify('\ud83d') writes U+FFFD. A circular structure's TypeError from JSON.stringify has an empty message.
  • 'ΑΣ'.toLowerCase() ends in σ, not ς (iOS).

Other differences ​

  • error.stack lists native frames, not JavaScript ones (see iOS and Android).
  • An object literal's own valueOf isn't called by < or Math.max, and its [Symbol.toPrimitive] is ignored (iOS). A Date held as any gives NaN in arithmetic. Call .valueOf() or .getTime().
  • Subclass fields are set before the base constructor runs (iOS). The build warns where an app's class relies on this.
  • typeof new String('x') is 'string', not 'object'.
  • String(Symbol('s')) gives @@s#1 (iOS). JSON.stringify writes a symbol value, where JavaScript leaves it out.
  • new RegExp('a/b').source is a/b, not a\/b (iOS).
  • A TypeError's constructor.name is JSTypeError (Android). new Array(-1) and an over-long padStart throw an Error, not a RangeError (Android).
  • new Date(y, m, d, h) in the hour a daylight-saving change skips lands an hour early (iOS).
  • Object.assign(target, …) with a literal null among the sources stops the app (iOS).
  • A var declared in a loop head or a branch, and read through a closure before it is set, reads 0, '' or false, not undefined (Android). So does a let x: boolean; declared without a value.
  • On Android, minification can remove a member reached only by a name computed at run time (R8).

Not fixed yet ​

The open items in the compiler's test suites, by what you would see.

Doesn't build yet (reported at your TypeScript line):

PatternPlatform
Untyped callbacks called with JavaScript's arity, and thisArgiOS, Android
A prototype method called on another receiver (Array.prototype.slice.call(list, 1), X.prototype.m.call(r))iOS (some forms), Android
Destructuring assignment to existing variables in some forms; a rest element in one ([first, ...others] = list)iOS, Android
Properties given to a function value (fn.tag = …)iOS
Top-level names declared by several modules (an enum, files of the same name in two folders)iOS
null as string | null in a template or a concatenationiOS
A sort comparator declared with fewer parameters than sort passesiOS
RegExp(…) without new, and indexing an exec() result without !Android
A built-in error constructor held as a value (const E: ErrorConstructor = TypeError), and error.constructor.nameAndroid
A function with a rest parameter passed to then (then(log))Android

Builds, behaves differently:

PatternPlatform
Object.defineProperty on a class instance, an array or a function is droppediOS, Android
delete arr[i], and delete of a declared memberiOS
A string parameter given undefined reads ''; a void promise's value isn't undefinediOS
An object literal an async function returns isn't made as its declared typeiOS
An optional member set after the object is made is listed in declaration order, not lastiOS
Object.keys lists the holes of new Array(n)iOS
A native class's own method declared with a non-optional result throws when it returns undefinediOS
typeof of a function read from an optional member, or of a narrowed string | string[] parameter, is decided at build time and can be wrongAndroid
JSON.parse and JSON.stringify edges: number forms, escapes, toJSON and replacer order, syntax error messagesAndroid

Checking a compiled release ​

You develop on the JavaScript runtime and ship compiled code, so a difference shows only in the release. Compare each compiled release with its JavaScript release before you ship it:

  1. Build both, ns build ios --release and ns build ios --compiled, with the same @nativescript/core. They share the app id, so install them on separate simulators or uninstall between them.
  2. Walk the same screens and taps in both, focusing and typing in every input, and watch for crashes as well as pixels.

ns compiled verify does this on an iOS Simulator. It builds both releases, runs the same steps on each (from the project's verify.json: taps, swipes, typing, waits and shots, each screen from a fresh launch), compares every shot pixel by pixel, and fails on a difference or on an app that stops running. The screenshots and a report.json go to platforms/compiled/verify.

json
{
  "screens": [
    {
      "name": "home",
      "steps": [
        ["shot", "start"],
        ["tap", 200, 400],
        ["wait", 1],
        ["type", "Ada"],
        ["shot", "typed"]
      ]
    }
  ]
}

Found a difference that isn't listed here? Report it.

Previous
Android