Vue but underthehood: Part 2 - The Language Engine
Introduction
Welcome back to "Vue but underthehood"! In Part 1, we explored how Vue 3 uses JavaScript Proxies as its reactive foundation. Now, let's dive into The Language Engine - how TypeScript integrates with Vue to provide excellent type safety and developer experience.
Vue 3's TypeScript-First Approach
Unlike Vue 2, which was written in JavaScript and had TypeScript definitions added later, Vue 3 was rewritten from the ground up in TypeScript. This fundamental shift brings numerous benefits:
- Better type inference for component props, emits, and refs
- Improved IDE support with autocomplete and error checking
- Self-documenting code through type annotations
- Safer refactoring with compile-time error detection
Type-Safe Component Props
Vue 3 provides excellent type inference for component props using TypeScript:
import { defineComponent, PropType } from "vue";
interface User {
id: number;
name: string;
email: string;
}
export default defineComponent({
props: {
user: {
type: Object as PropType<User>,
required: true,
},
count: {
type: Number,
default: 0,
},
},
setup(props) {
// props.user is typed as User
// props.count is typed as number
console.log(props.user.name); // ✅ Type-safe!
},
});
Script Setup and Type Inference
The <script setup> syntax provides even better type inference with less boilerplate:
<script setup lang="ts">
import { ref, computed } from 'vue';
interface Todo {
id: number;
text: string;
completed: boolean;
}
const props = defineProps<{
initialTodos: Todo[];
maxCount?: number;
}>();
const todos = ref<Todo[]>(props.initialTodos);
const completedTodos = computed(() =>
todos.value.filter(todo => todo.completed)
);
// Everything is fully typed!
</script>
Generic Components
Vue 3.3+ introduces support for generic components, allowing for even more flexible typing:
<script setup lang="ts" generic="T">
defineProps<{
items: T[];
selectedItem: T | null;
}>();
const emit = defineEmits<{
select: [item: T];
remove: [item: T];
}>();
</script>
The Ref Type System
Understanding how Vue types refs is crucial:
import { ref, Ref, unref } from "vue";
// Ref<number>
const count = ref(0);
// Ref<string | null>
const message = ref<string | null>(null);
// Type narrowing works!
if (message.value) {
// message.value is typed as string here
console.log(message.value.toUpperCase());
}
// unref utility for unwrapping
function useValue<T>(maybeRef: T | Ref<T>): T {
return unref(maybeRef);
}
Internal Type Utilities
Vue provides powerful internal type utilities that you can leverage:
import type {
ExtractPropTypes,
ExtractPublicPropTypes,
ComponentPublicInstance,
} from "vue";
const props = {
name: String,
age: Number,
active: Boolean,
} as const;
// Extract the prop types
type Props = ExtractPropTypes<typeof props>;
// { name?: string; age?: number; active?: boolean }
type PublicProps = ExtractPublicPropTypes<typeof props>;
// For external usage
Key Takeaways
- Vue 3 is written in TypeScript, providing first-class type support
defineComponentand<script setup>offer excellent type inference- Generic components enable flexible, reusable typed components
- Vue's type utilities help extract and work with component types
- The TypeScript integration enhances both DX and code safety
What's Next?
In Part 3: The Safety Valve, we'll explore Vue's error handling mechanisms and how it gracefully handles failures at runtime.
This is Part 2 of the "Vue but underthehood" series.
