The Problem

Vue prop validation warns when a parent passes a value of the wrong type. The most common mismatch is passing a string (e.g., from HTML attributes or API data) where a number is expected.

Symptoms

  • Console warning: "Invalid prop: type check failed for prop X. Expected Number, got String"
  • Mathematical operations return NaN or concatenated strings
  • Comparisons fail (e.g., 5 === '5' is false)
  • Component behavior is incorrect but no crash

Real Error Message

bash
[Vue warn]: Invalid prop: type check failed for prop "count".
Expected Number with value 5, got String with value "5".
  at <Counter count="5" >

Common Causes

Cause 1: HTML Attributes Are Always Strings

```vue <!-- WRONG: This passes the STRING "5", not the number 5 --> <Counter count="5" />

<!-- CORRECT: Use v-bind for numbers --> <Counter :count="5" /> ```

Cause 2: API Data Returns String Numbers

```javascript // API returns: { id: "42", quantity: "10" } const data = await fetch('/api/products').then(r => r.json());

// WRONG: Passing string numbers to number props <ProductCard :price="data.price" :quantity="data.quantity" /> ```

Cause 3: Route Params Are Strings

```javascript // URL: /products/42 // route.params.id is the STRING "42"

// WRONG <ProductDetail :productId="$route.params.id" /> ```

How to Fix It

Fix 1: Use v-bind for Literal Numbers

vue
<Counter :count="5" />
<Grid :columns="3" :gap="16" />
<Timer :duration="30" />

Fix 2: Type Coerce in the Child Component

```vue <script setup> const props = defineProps({ count: { type: [Number, String], required: true, validator: (value) => !isNaN(Number(value)) } });

// Coerce to number internally const numericCount = computed(() => Number(props.count)); </script> ```

Fix 3: Coerce at the Call Site

vue
<ProductDetail :productId="Number($route.params.id)" />
<ProductCard :price="Number(data.price)" :quantity="Number(data.quantity)" />

Fix 4: Use a Composable for API Data Transformation

javascript
function useTypedProducts(apiData) {
  return computed(() => apiData.map(p => ({
    ...p,
    id: Number(p.id),
    price: Number(p.price),
    quantity: Number(p.quantity)
  })));
}

Strict Prop Validation

javascript
defineProps({
  count: {
    type: Number,
    required: true,
    validator: (value) => Number.isInteger(value) && value > 0
  }
});