TypeScript
The blank template is set up for TypeScript out of the box:
ns create myApp --template @nativescript-vue/template-blank@latestTypeScript is not a requirement — see Using JavaScript at the end of this page.
nativescript-vue ships its own type declarations, and it re-exports the whole Vue API (ref, computed, watch, defineComponent, the lifecycle hooks, ...) from @vue/runtime-core. Import them from nativescript-vue:
import { ref, computed, onMounted } from 'nativescript-vue';tsconfig
The parts of the template's tsconfig.json that matter for NativeScript-Vue:
{
"compilerOptions": {
"lib": ["esnext", "WebWorker"],
"paths": {
"~/*": ["./src/*"],
"@/*": ["./src/*"],
"vue": ["./node_modules/nativescript-vue"]
}
},
"vueCompilerOptions": {
"target": 3,
"lib": "nativescript-vue"
},
"include": ["src", "types"],
"exclude": ["node_modules", "platforms"]
}paths — mapping vue to nativescript-vue
There is no vue package in a NativeScript-Vue project. Libraries such as Pinia or vue-i18n import from — and augment — the vue module, so without this mapping their types do not line up with the runtime the app actually uses: app.use(pinia) reports a type error, augmentations of ComponentCustomProperties never reach your components, and import { ref } from 'vue' fails to resolve. The webpack integration aliases vue to nativescript-vue at build time in the same way, so the types match what is bundled.
Prefer the mapping over installing vue itself. When a vue package is present, vue-loader compiles templates with that package's compiler, and the build warns when its version differs from the @vue/runtime-core version nativescript-vue runs on.
vueCompilerOptions
Volar — vue-tsc and the Vue VS Code extension — generates its type-checking code from the lib package's defineComponent, which is why lib points at nativescript-vue. Setting target: 3 explicitly avoids Volar trying to detect the Vue version from a vue package that isn't installed.
lib without dom
NativeScript's runtime has no browser DOM, so the template uses ["esnext", "WebWorker"]. Including dom would let code compile against APIs that do not exist at runtime.
The types folder
include covers both src and types. The types folder holds two declaration files.
types/references.d.ts pulls in the native iOS and Android API typings from @nativescript/types (a devDependency):
/// <reference path="../node_modules/@nativescript/types/index.d.ts" />types/shims.vue.d.ts declares the module type for *.vue imports:
declare module '*.vue' {
import type { DefineComponent } from 'nativescript-vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}Existing projects
Add the paths entry and the vueCompilerOptions block to your tsconfig.json:
{
"compilerOptions": {
"paths": {
"vue": ["./node_modules/nativescript-vue"]
}
},
"vueCompilerOptions": {
"lib": "nativescript-vue"
}
}Typing components
Props and emits work exactly as in Vue, using the type-only forms of defineProps and defineEmits:
<script setup lang="ts">
const props = defineProps<{ id: number }>();
const emit = defineEmits<{ saved: [item: Item] }>();
</script>Template refs resolve to a wrapper — the native view lives on .nativeView, which you cast to the matching class from @nativescript/core (see Template Refs):
<script setup lang="ts">
import { useTemplateRef, onMounted } from 'nativescript-vue';
import { type Label } from '@nativescript/core';
const label = useTemplateRef('my-label');
onMounted(() => {
const labelView = label.value.nativeView as Label;
labelView.text = 'Text changed';
});
</script>
<template>
<Label ref="my-label" text="Hello" />
</template>The same cast applies to event handlers, where $event.object is the native view that raised the event:
<script setup lang="ts">
import type { EventData, TextField } from '@nativescript/core';
function onTextChange(event: EventData) {
console.log((event.object as TextField).text);
}
</script>
<template>
<TextField @textChange="onTextChange" />
</template>ListView ships two helper types — ListItem<T> for the slot scope and ListViewItemTapEvent<T> for @itemTap handlers (see ListView):
<script setup lang="ts">
import type { ListItem, ListViewItemTapEvent } from 'nativescript-vue';
function onItemTap(event: ListViewItemTapEvent<Item>) {
console.log(event.item.name, event.index);
}
</script>
<template>
<ListView :items="items" @itemTap="onItemTap">
<template #default="{ item }: ListItem<Item>">
<Label :text="item.name" />
</template>
</ListView>
</template>Type checking
For a full check of both .ts files and .vue templates, add vue-tsc as a devDependency and run:
npx vue-tsc --noEmitDuring ns run, the webpack build reports TypeScript errors for .ts files through fork-ts-checker, but it does not type-check .vue templates — the NativeScript-Vue webpack integration disables the checker's Vue mode. Rely on vue-tsc or your editor for template errors.
In VS Code, install the Vue (official) extension.
Using JavaScript
Everything on this page is optional — NativeScript-Vue works without TypeScript:
- Rename
src/app.tstosrc/app.jsand pointmaininpackage.json(or innativescript.config) at it. - Use
<script setup>withoutlang="ts". - Don't install
typescript. The webpack integration only enables the TypeScript checker when the project depends ontypescript; JavaScript-only projects were fixed to build without it in 3.1.0.