Vize

Type & Script Rules

Type rules use the TypeScript checker when semantic information is needed. Vize reads the same project shape that TypeScript reads from tsconfig.json, so shared ambient names should come from compilerOptions.types, project references, or declaration files.

Script rules are Patina rules for Composition API and Vapor-oriented code. They focus on patterns that are hard to compile efficiently or hard to reason about in Vapor mode.

type/require-typed-props

Requires defineProps to be typed instead of using a runtime array declaration.

Default severity: warning Presets: happy-path, nuxt, opinionated

Bad:

<script setup lang="ts">
const props = defineProps(["label", "count"]);
</script>

Good:

<script setup lang="ts">
const props = defineProps<{
  label: string;
  count: number;
}>();
</script>

type/require-typed-emits

Requires defineEmits to describe the emitted event payloads.

Default severity: warning Presets: happy-path, nuxt, opinionated

Bad:

<script setup lang="ts">
const emit = defineEmits(["save"]);

emit("save", form.value);
</script>

Good:

<script setup lang="ts">
const emit = defineEmits<{
  save: [payload: FormValue];
}>();

emit("save", form.value);
</script>

type/no-unsafe-template-binding

Reports template bindings that resolve to unsafe values such as any. The rule is checker-backed, so it follows imported types and project configuration.

Default severity: warning Presets: nuxt, opinionated

Bad:

<script setup lang="ts">
const payload: any = await loadPayload();
</script>

<template>
  <p>{{ payload.title }}</p>
</template>

Good:

<script setup lang="ts">
type Payload = { title: string };

const payload = await loadPayload<Payload>();
</script>

<template>
  <p>{{ payload.title }}</p>
</template>

type/no-floating-promises

Reports promises that are created but not awaited, returned, or intentionally handled. The check covers both <script> and template expressions.

Default severity: warning Presets: nuxt, opinionated

Bad:

<script setup lang="ts">
function submit() {
  saveForm(form.value);
}
</script>

<template>
  <button @click="saveForm(form)">Save</button>
  <p>{{ loadPreview() }}</p>
</template>

Good:

<script setup lang="ts">
type Preview = { title: string };

async function submit() {
  await saveForm(form.value);
}

const preview = ref<Preview | null>(null);

async function loadPreviewIntoState() {
  preview.value = await loadPreview();
}
</script>

<template>
  <button @click="void submit()">Save</button>
  <button @click="void loadPreviewIntoState()">Preview</button>
  <PreviewPanel v-if="preview" :preview="preview" />
</template>

type/no-reactivity-loss

Reports plain snapshots of reactive values that are used across flows. The rule also runs when vize lint --strict-reactivity is enabled.

Default severity: warning Presets: nuxt, opinionated

Bad:

<script setup lang="ts">
const props = defineProps<{ item: { name: string } }>();
const item = props.item;
</script>

Good:

<script setup lang="ts">
const props = defineProps<{ item: { name: string } }>();
const item = toRef(props, "item");
</script>

Checker Configuration

The type-aware rules do not need a separate Vize globals field for TypeScript names. Prefer TypeScript-native configuration:

Bad:

export default {
  globals: ["definePageMeta", "process"],
};

Good:

{
  "compilerOptions": {
    "types": ["node", "nuxt/app"]
  }
}

script/no-options-api

Reports Options API component definitions in Vapor-oriented presets.

Default severity: error Presets: nuxt, opinionated

Bad:

<script lang="ts">
export default {
  data() {
    return { count: 0 };
  },
};
</script>

Good:

<script setup lang="ts" vapor>
const count = ref(0);
</script>

script/no-next-tick

Reports nextTick() in Vapor-oriented components. Prefer direct refs, lifecycle hooks, or state flow that does not depend on the next DOM flush.

Default severity: error Presets: nuxt, opinionated

Bad:

<script setup lang="ts" vapor>
await nextTick();
input.value?.focus();
</script>

Good:

<script setup lang="ts" vapor>
const input = useTemplateRef<HTMLInputElement>("input");

onMounted(() => {
  input.value?.focus();
});
</script>

script/no-get-current-instance

Reports getCurrentInstance() in Vapor-oriented components. It reaches into runtime internals that Vapor cannot safely optimize.

Default severity: error Presets: nuxt, opinionated

Bad:

<script setup lang="ts" vapor>
const instance = getCurrentInstance();
const app = instance?.appContext.app;
</script>

Good:

<script setup lang="ts" vapor>
const appConfig = useAppConfig();
</script>