Vue 50: TypeScript with Vue
easy⏱ 5 mincoursevue
Typed refs and composables
A Ref<T> is just the type of what ref() returns, with its .value type fixed explicitly. A typed composable declares the exact shape it returns, so anyone calling it from another component gets autocomplete and a type error if something doesn't fit.
import type { Ref } from 'vue';
import { ref } from 'vue';
function useCounter(initial: number): { count: Ref<number>; increment: () => void } {
const count: Ref<number> = ref(initial);
const increment = () => { count.value++; };
return { count, increment };
}
Typed props: two flavors
In <script setup>, defineProps<{...}>() takes the props' shape as a generic type argument and Vue infers it at compile time. In the Options API (what this sandbox uses), you annotate at runtime instead, with PropType<T> inside each prop's definition.
// <script setup> - inferred from a generic type argument
defineProps<{ label: string; tone?: string }>()
// Options API - annotate at runtime with PropType<T>
export default {
props: {
label: { type: String as PropType<string>, required: true },
},
}
Babel strips types, it doesn't check them
In this sandbox, Babel's TypeScript preset only erases type annotations - it does not type-check them. A real project would also run vue-tsc --noEmit (or your editor's TS server) to catch type errors before they ship. Here, if you write invalid types, the code will probably still run, because nothing verifies them at runtime.
Why bother
Real autocomplete in your editor, safer refactors, self-documenting composables, and prop-misuse errors caught in CI before a user ever sees them - that's the payoff for writing Vue with types.