Skip to content

TypeScript with Composition API ​

āĻāχ āĻĒ⧃āĻˇā§āĻ āĻžāϟāĻŋ āϧāϰ⧇ āύāĻŋāĻšā§āϛ⧇ āϝ⧇ āφāĻĒāύāĻŋ āχāϤāĻŋāĻŽāĻ§ā§āϝ⧇āχ Using Vue with TypeScript āĻāϰ āĻ“āĻ­āĻžāϰāĻ­āĻŋāωāϟāĻŋ āĻĒāĻĄāĻŧ⧇āϛ⧇āύāĨ¤

Typing Component Props ​

Using <script setup> ​

<script setup> āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ, defineProps() āĻŽā§āϝāĻžāĻ•ā§āϰ⧋ āϤāĻžāϰ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āĻĒā§āϰāĻĒ āϧāϰāύ⧇āϰ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāĻž āϏāĻŽāĻ°ā§āĻĨāύ āĻ•āϰ⧇:

vue
<script setup lang="ts">
const props = defineProps({
  foo: { type: String, required: true },
  bar: Number
})

props.foo // string
props.bar // number | undefined
</script>

āĻāϟāĻŋāϕ⧇ "runtime declaration" āĻŦāϞāĻž āĻšāϝāĻŧ, āĻ•āĻžāϰāĻŖ defineProps()-āĻ āĻĒāĻžāϏ āĻ•āϰāĻž āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āϰāĻžāύāϟāĻžāχāĻŽ props āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšāĻŦ⧇āĨ¤

āϝāĻžāχāĻšā§‹āĻ•, āϏāĻžāϧāĻžāϰāĻŖāϤ āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• āϟāĻžāχāĻĒ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻŦāĻŋāĻļ⧁āĻĻā§āϧ āĻĒā§āϰāĻ•āĻžāϰ⧇āϰ āϏāĻžāĻĨ⧇ āĻĒā§āϰāĻĒāϏāϕ⧇ āϏāĻ‚āĻœā§āĻžāĻžāϝāĻŧāĻŋāϤ āĻ•āϰāĻž āφāϰāĻ“ āϏ⧋āϜāĻž:

vue
<script setup lang="ts">
const props = defineProps<{
  foo: string
  bar?: number
}>()
</script>

āĻāϕ⧇ "type-based declaration" āĻŦāϞāĻž āĻšāϝāĻŧāĨ¤ āĻ•āĻŽā§āĻĒāĻžāχāϞāĻžāϰ āϟāĻžāχāĻĒ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āϏāĻŽāϤ⧁āĻ˛ā§āϝ āϰāĻžāύāϟāĻžāχāĻŽ āĻŦāĻŋāĻ•āĻ˛ā§āĻĒāϗ⧁āϞāĻŋ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ āϝāĻĨāĻžāϏāĻžāĻ§ā§āϝ āĻšā§‡āĻˇā§āϟāĻž āĻ•āϰāĻŦ⧇āĨ¤ āĻāχ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇, āφāĻŽāĻžāĻĻ⧇āϰ āĻĻā§āĻŦāĻŋāϤ⧀āϝāĻŧ āωāĻĻāĻžāĻšāϰāĻŖāϟāĻŋ āĻĒā§āϰāĻĨāĻŽ āωāĻĻāĻžāĻšāϰāϪ⧇āϰ āĻŽāϤ⧋ āĻāĻ•āχ āϰāĻžāύāϟāĻžāχāĻŽ āĻŦāĻŋāĻ•āĻ˛ā§āĻĒāϗ⧁āϞāĻŋāϤ⧇ āĻ•āĻŽā§āĻĒāĻžāχāϞ āĻ•āϰ⧇āĨ¤

āφāĻĒāύāĻŋ type-based declaration āĻŦāĻž runtime declaration āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ, āĻ•āĻŋāĻ¨ā§āϤ⧁ āφāĻĒāύāĻŋ āĻāĻ•āχ āϏāĻŽāϝāĻŧ⧇ āωāĻ­āϝāĻŧāχ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŦ⧇āύ āύāĻžāĨ¤

āφāĻŽāϰāĻž āĻāĻ•āϟāĻŋ āĻĒ⧃āĻĨāĻ• āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ⧇ āĻĒā§āϰāĻĒāϏ āĻĒā§āϰāĻ•āĻžāϰāϗ⧁āϞāĻŋāϕ⧇ āĻ¸ā§āĻĨāĻžāύāĻžāĻ¨ā§āϤāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŋ:

vue
<script setup lang="ts">
interface Props {
  foo: string
  bar?: number
}

const props = defineProps<Props>()
</script>

āĻāϟāĻŋāĻ“ āĻ•āĻžāϜ āĻ•āϰ⧇ āϝāĻĻāĻŋ Props āϕ⧋āύ⧋ āĻŦāĻžāĻšā§āϝāĻŋāĻ• āĻ‰ā§ŽāϏ āĻĨ⧇āϕ⧇ āχāĻŽā§āĻĒā§‹āĻ°ā§āϟ āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ āĻāχ āĻŦ⧈āĻļāĻŋāĻˇā§āĻŸā§āϝāϟāĻŋāϰ āϜāĻ¨ā§āϝ Vue-āĻāϰ peer āύāĻŋāĻ°ā§āĻ­āϰāϤāĻž āĻšāϤ⧇ TypeScript āĻĒā§āϰāϝāĻŧā§‹āϜāύāĨ¤

vue
<script setup lang="ts">
import type { Props } from './foo'

const props = defineProps<Props>()
</script>

Syntax Limitations ​

āϏāĻ‚āĻ¸ā§āĻ•āϰāĻŖ 3.2 āĻāĻŦāĻ‚ āύ⧀āĻšā§‡, defineProps()-āĻāϰ āϜāĻ¨ā§āϝ āĻœā§‡āύ⧇āϰāĻŋāĻ• āϟāĻžāχāĻĒ āĻĒā§āϝāĻžāϰāĻžāĻŽāĻŋāϟāĻžāϰ āĻāĻ•āϟāĻŋ āφāĻ•ā§āώāϰāĻŋāĻ• āϟāĻžāχāĻĒ āĻŦāĻž āĻ¸ā§āĻĨāĻžāύ⧀āϝāĻŧ āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ⧇āϰ āĻāĻ•āϟāĻŋ āϰ⧇āĻĢāĻžāϰ⧇āĻ¨ā§āϏ⧇āϰ āĻŽāĻ§ā§āϝ⧇ āϏ⧀āĻŽāĻžāĻŦāĻĻā§āϧ āĻ›āĻŋāϞāĨ¤

āĻāχ āϏ⧀āĻŽāĻžāĻŦāĻĻā§āϧāϤāĻž 3.3 āĻ āϏāĻŽāĻžāϧāĻžāύ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇āĨ¤ Vue-āĻāϰ āϏāĻ°ā§āĻŦāĻļ⧇āώ āϏāĻ‚āĻ¸ā§āĻ•āϰāĻŖāϟāĻŋ āϟāĻžāχāĻĒ āĻĒā§āϝāĻžāϰāĻžāĻŽāĻŋāϟāĻžāϰ āĻ…āĻŦāĻ¸ā§āĻĨāĻžāύ⧇ āφāĻŽāĻĻāĻžāύāĻŋ āĻ•āϰāĻž āϰ⧇āĻĢāĻžāϰ⧇āĻ¨ā§āϏāĻŋāĻ‚ āĻāĻŦāĻ‚ āϜāϟāĻŋāϞ āϧāϰāύ⧇āϰ āĻāĻ•āϟāĻŋ āϏ⧀āĻŽāĻŋāϤ āϏ⧇āϟ āϏāĻŽāĻ°ā§āĻĨāύ āĻ•āϰ⧇āĨ¤ āϝāĻžāχāĻšā§‹āĻ•, āϝ⧇āĻšā§‡āϤ⧁ āϰāĻžāύāϟāĻžāχāĻŽ āϰ⧂āĻĒāĻžāĻ¨ā§āϤāϰ⧇āϰ āϧāϰāύāϟāĻŋ āĻāĻ–āύāĻ“ AST-āĻ­āĻŋāĻ¤ā§āϤāĻŋāĻ•, āĻ•āĻŋāϛ⧁ āϜāϟāĻŋāϞ āĻĒā§āϰāĻ•āĻžāϰ⧇āϰ āĻĒā§āϰāĻ•ā§ƒāϤ āĻĒā§āϰāĻ•āĻžāϰ āĻŦāĻŋāĻļā§āϞ⧇āώāϪ⧇āϰ āĻĒā§āϰāϝāĻŧā§‹āϜāύ, āϝ⧇āĻŽāύ āĻļāĻ°ā§āϤāĻžāϧ⧀āύ āĻĒā§āϰāĻ•āĻžāϰ, āϏāĻŽāĻ°ā§āĻĨāĻŋāϤ āύāϝāĻŧāĨ¤ āφāĻĒāύāĻŋ āĻāĻ•āϟāĻŋ āĻāĻ•āĻ• āĻĒā§āϰāĻĒ⧇āϰ āϧāϰāϪ⧇āϰ āϜāĻ¨ā§āϝ āĻļāĻ°ā§āϤāϏāĻžāĻĒ⧇āĻ•ā§āώ āĻĒā§āϰāĻ•āĻžāϰāϗ⧁āϞāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ, āϤāĻŦ⧇ āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻĒā§āϰāĻĒāϏ āĻ…āĻŦāĻœā§‡āĻ•ā§āϟ āύāϝāĻŧāĨ¤

Props Default Values ​

āϟāĻžāχāĻĒ-āĻ­āĻŋāĻ¤ā§āϤāĻŋāĻ• āĻ˜ā§‹āώāĻŖāĻž āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ, āφāĻŽāϰāĻž āĻĒā§āϰāĻĒāϏ⧇āϰ āϜāĻ¨ā§āϝ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻŽāĻžāύ āĻ˜ā§‹āώāĻŖāĻž āĻ•āϰāĻžāϰ āĻ•ā§āώāĻŽāϤāĻž āĻšāĻžāϰāĻŋāϝāĻŧ⧇ āĻĢ⧇āϞāĻŋāĨ¤ Reactive Props Destructure āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻāϟāĻŋ āϏāĻŽāĻžāϧāĻžāύ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇:

ts
interface Props {
  msg?: string
  labels?: string[]
}

const { msg = 'hello', labels = ['one', 'two'] } = defineProps<Props>()

In 3.4 and below, Reactive Props Destructure is not enabled by default. An alternative is to use the withDefaults compiler macro:

ts
interface Props {
  msg?: string
  labels?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})

āĻāϟāĻŋ āϏāĻŽāϤ⧁āĻ˛ā§āϝ āϰāĻžāύāϟāĻžāχāĻŽ āĻĒā§āϰāĻĒāϏ default āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ⧇ āĻ•āĻŽā§āĻĒāĻžāχāϞ āĻ•āϰāĻž āĻšāĻŦ⧇āĨ¤ āωāĻĒāϰāĻ¨ā§āϤ⧁, withDefaults āĻšā§‡āĻ˛ā§āĻĒāĻžāϰ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻŽāĻžāύāϗ⧁āϞāĻŋāϰ āϜāĻ¨ā§āϝ āϟāĻžāχāĻĒ āĻšā§‡āĻ• āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰ⧇ āĻāĻŦāĻ‚ āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰ⧇ āϝ⧇ āĻĒā§āϰāĻ¤ā§āϝāĻžāĻŦāĻ°ā§āϤāĻŋāϤ props āϟāĻžāχāĻĒāϟāĻŋāϤ⧇ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻŽāĻžāύ āĻ˜ā§‹āώāĻŋāϤ āĻŦ⧈āĻļāĻŋāĻˇā§āĻŸā§āϝāϗ⧁āϞāĻŋāϰ āϜāĻ¨ā§āϝ āϐāĻšā§āĻ›āĻŋāĻ• āĻĢā§āĻ˛ā§āϝāĻžāĻ—āϗ⧁āϞāĻŋ āϏāϰāĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇āĨ¤

INFO

āĻŽāύ⧇ āϰāĻžāĻ–āĻŦ⧇āύ āϝ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύāϝ⧋āĻ—ā§āϝ āϰ⧇āĻĢāĻžāϰ⧇āĻ¨ā§āϏ āĻĒā§āϰāĻ•āĻžāϰ⧇āϰ (āϝ⧇āĻŽāύ āĻ…ā§āϝāĻžāϰ⧇ āĻŦāĻž āĻ…āĻŦāĻœā§‡āĻ•ā§āϟ) āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻŽāĻžāύāϗ⧁āϞāĻŋ āĻĻ⧁āĻ°ā§āϘāϟāύāĻžāϜāύāĻŋāϤ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻāĻŦāĻ‚ āĻŦāĻžāĻšā§āϝāĻŋāĻ• āĻĒāĻžāĻ°ā§āĻļā§āĻŦ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž āĻāĻĄāĻŧāĻžāϤ⧇ withDefaults āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āĻĢāĻžāĻ‚āĻļāύ⧇ āĻŽā§‹āĻĄāĻŧāĻžāύ⧋ āωāϚāĻŋāϤāĨ¤ āĻāϟāĻŋ āύāĻŋāĻļā§āϚāĻŋāϤ āĻ•āϰ⧇ āϝ⧇ āĻĒā§āϰāϤāĻŋāϟāĻŋ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ āĻĻ⧃āĻˇā§āϟāĻžāĻ¨ā§āϤ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻŽāĻžāύ⧇āϰ āύāĻŋāϜāĻ¸ā§āĻŦ āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻĒāĻžāϝāĻŧāĨ¤ āĻ§ā§āĻŦāĻ‚āϏ⧇āϰ āϏāĻžāĻĨ⧇ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻŽāĻžāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āĻāϟāĻŋ āĻĒā§āϰāϝāĻŧā§‹āϜāύ⧀āϝāĻŧ āύāϝāĻŧāĨ¤

Without <script setup> ​

<script setup> āĻŦā§āϝāĻŦāĻšāĻžāϰ āύāĻž āĻ•āϰāϞ⧇, āĻĒā§āϰāĻĒ āϟāĻžāχāĻĒ āχāύāĻĢāĻžāϰ⧇āĻ¨ā§āϏ āϏāĻ•ā§āώāĻŽ āĻ•āϰāϤ⧇ defineComponent() āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻšāĻŦ⧇āĨ¤ āĻĒā§āϰāĻĒāϏ āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸā§‡āϰ āϧāϰāύ āϝāĻž setup()-āĻ āĻĒāĻžāϏ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇ āϤāĻž props āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ āĻĨ⧇āϕ⧇ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇āĨ¤

ts
import { defineComponent } from 'vue'

export default defineComponent({
  props: {
    message: String
  },
  setup(props) {
    props.message // <-- type: string
  }
})

Complex prop types ​

āϟāĻžāχāĻĒ-āĻ­āĻŋāĻ¤ā§āϤāĻŋāĻ• āĻ˜ā§‹āώāĻŖāĻžāϰ āϏāĻžāĻĨ⧇, āĻāĻ•āϟāĻŋ āĻĒā§āϰāĻĒ āĻāĻ•āϟāĻŋ āϜāϟāĻŋāϞ āϟāĻžāχāĻĒ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇ āϝ⧇āĻŽāύ āĻ…āĻ¨ā§āϝ āϝ⧇āϕ⧋āύ⧋ āϧāϰāύ⧇āϰ:

vue
<script setup lang="ts">
interface Book {
  title: string
  author: string
  year: number
}

const props = defineProps<{
  book: Book
}>()
</script>

For runtime declaration, āφāĻŽāϰāĻž PropType āχāωāϟāĻŋāϞāĻŋāϟāĻŋ āϟāĻžāχāĻĒ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŋ:

ts
import type { PropType } from 'vue'

const props = defineProps({
  book: Object as PropType<Book>
})

āĻāϟāĻŋ āĻāĻ•āχāĻ­āĻžāĻŦ⧇ āĻ•āĻžāϜ āĻ•āϰ⧇ āϝāĻĻāĻŋ āφāĻŽāϰāĻž āϏāϰāĻžāϏāϰāĻŋ props āĻŦāĻŋāĻ•āĻ˛ā§āĻĒāϟāĻŋ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻ•āϰāĻ›āĻŋ:

ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'

export default defineComponent({
  props: {
    book: Object as PropType<Book>
  }
})

āĻ…āĻĒāĻļāύ āĻāĻĒāĻŋāφāχ-āĻāϰ āϏāĻžāĻĨ⧇ props āĻŦāĻŋāĻ•āĻ˛ā§āĻĒāϟāĻŋ āĻŦ⧇āĻļāĻŋ āĻŦā§āϝāĻŦāĻšā§ƒāϤ āĻšāϝāĻŧ, āϤāĻžāχ āφāĻĒāύāĻŋ TypeScript with Options API āĻāϰ āĻ—āĻžāχāĻĄā§‡ āφāϰāĻ“ āĻŦāĻŋāĻ¸ā§āϤāĻžāϰāĻŋāϤ āωāĻĻāĻžāĻšāϰāĻŖ āĻĒāĻžāĻŦ⧇āύāĨ¤ āĻāχ āωāĻĻāĻžāĻšāϰāĻŖāϗ⧁āϞāĻŋāϤ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻ•ā§ŒāĻļāϞāϗ⧁āϞāĻŋ defineProps() āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āϰāĻžāύāϟāĻžāχāĻŽ āĻ˜ā§‹āώāĻŖāĻžāϰ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇āĻ“ āĻĒā§āϰāϝ⧋āĻœā§āϝāĨ¤

Typing Component Emits ​

<script setup>-āĻ, emit āĻĢāĻžāĻ‚āĻļāύāϟāĻŋ āϰāĻžāύāϟāĻžāχāĻŽ āĻ˜ā§‹āώāĻŖāĻž āĻŦāĻž āϟāĻžāχāĻĒ āĻ˜ā§‹āώāĻŖāĻž āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇āĻ“ āϟāĻžāχāĻĒ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇:

vue
<script setup lang="ts">
// runtime
const emit = defineEmits(['change', 'update'])

// options based
const emit = defineEmits({
  change: (id: number) => {
    // return `true` or `false` to indicate
    // validation pass / fail
  },
  update: (value: string) => {
    // return `true` or `false` to indicate
    // validation pass / fail
  }
})

// type-based
const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string): void
}>()

// 3.3+: alternative, more succinct syntax
const emit = defineEmits<{
  change: [id: number]
  update: [value: string]
}>()
</script>

āϟāĻžāχāĻĒ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āύāĻŋāĻŽā§āύāϞāĻŋāĻ–āĻŋāϤāϗ⧁āϞāĻŋāϰ āĻŽāĻ§ā§āϝ⧇ āĻāĻ•āϟāĻŋ āĻšāϤ⧇ āĻĒāĻžāϰ⧇:

  1. āĻāĻ•āϟāĻŋ āĻ•āϞāϝ⧋āĻ—ā§āϝ āĻĢāĻžāĻ‚āĻļāύ āĻĒā§āϰāĻ•āĻžāϰ, āĻ•āĻŋāĻ¨ā§āϤ⧁ Call Signatures āϏāĻš āφāĻ•ā§āώāϰāĻŋāĻ• āϟāĻžāχāĻĒ āĻšāĻŋāϏāĻžāĻŦ⧇ āϞ⧇āĻ–āĻžāĨ¤ āĻāϟāĻŋ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰāĻž āĻemitāĻŽāĻŋāϟ āĻĢāĻžāĻ‚āĻļāύ⧇āϰ āϧāϰāĻŖ āĻšāĻŋāϏ⧇āĻŦ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšāĻŦ⧇āĨ¤
  2. āĻāĻ•āϟāĻŋ āĻĒā§āϰāĻ•āĻžāϰ āφāĻ•ā§āώāϰāĻŋāĻ• āϝ⧇āĻ–āĻžāύ⧇ āϕ⧀āϗ⧁āϞāĻŋ āĻšāϞ āχāϭ⧇āĻ¨ā§āĻŸā§‡āϰ āύāĻžāĻŽ, āĻāĻŦāĻ‚ āĻŽāĻžāύāϗ⧁āϞāĻŋ āĻšāϞ āĻ…ā§āϝāĻžāϰ⧇/āϟ⧁āĻĒāϞ āĻĒā§āϰāĻ•āĻžāϰ āϝāĻž āχāϭ⧇āĻ¨ā§āĻŸā§‡āϰ āϜāĻ¨ā§āϝ āĻ…āϤāĻŋāϰāĻŋāĻ•ā§āϤ āĻ—ā§ƒāĻšā§€āϤ āĻĒāϰāĻžāĻŽāĻŋāϤāĻŋāϗ⧁āϞāĻŋāϕ⧇ āωāĻĒāĻ¸ā§āĻĨāĻžāĻĒāύ āĻ•āϰ⧇āĨ¤ āωāĻĒāϰ⧇āϰ āωāĻĻāĻžāĻšāϰāĻŖāϟāĻŋ āύāĻžāĻŽāϝ⧁āĻ•ā§āϤ tuples āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϛ⧇ āϤāĻžāχ āĻĒā§āϰāϤāĻŋāϟāĻŋ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āϰ āĻāĻ•āϟāĻŋ āĻ¸ā§āĻĒāĻˇā§āϟ āύāĻžāĻŽ āĻĨāĻžāĻ•āϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

āφāĻŽāϰāĻž āĻĻ⧇āĻ–āϤ⧇ āĻĒāĻžāĻšā§āĻ›āĻŋ, āϟāĻžāχāĻĒ āĻĄāĻŋāĻ•ā§āϞāĻžāϰ⧇āĻļāύ āφāĻŽāĻžāĻĻ⧇āϰ āύāĻŋāĻ°ā§āĻ—āϤ āχāϭ⧇āĻ¨ā§āĻŸā§‡āϰ āϟāĻžāχāĻĒ āϏ⧀āĻŽāĻžāĻŦāĻĻā§āϧāϤāĻžāϰ āωāĻĒāϰ āĻ…āύ⧇āĻ• āϏ⧂āĻ•ā§āĻˇā§āĻŽ āύāĻŋāϝāĻŧāĻ¨ā§āĻ¤ā§āϰāĻŖ āĻĻ⧇āϝāĻŧāĨ¤

<script setup> āĻŦā§āϝāĻŦāĻšāĻžāϰ āύāĻž āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ, defineComponent() āϏ⧇āϟāφāĻĒ āĻĒā§āϰāϏāĻ™ā§āϗ⧇ āĻĒā§āϰāĻ•āĻžāĻļāĻŋāϤ emit āĻĢāĻžāĻ‚āĻļāύ⧇āϰ āϜāĻ¨ā§āϝ āĻ…āύ⧁āĻŽā§‹āĻĻāĻŋāϤ āχāϭ⧇āĻ¨ā§āϟāϗ⧁āϞāĻŋ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāϤ⧇ āϏāĻ•ā§āώāĻŽ:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  emits: ['change'],
  setup(props, { emit }) {
    emit('change') // <-- type check / auto-completion
  }
})

Typing ref() ​

Refs āĻĒā§āϰāĻžāĻĨāĻŽāĻŋāĻ• āĻŽāĻžāύ āĻĨ⧇āϕ⧇ āĻĒā§āϰāĻ•āĻžāϰ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰ⧇:

ts
import { ref } from 'vue'

// inferred type: Ref<number>
const year = ref(2020)

// => TS Error: Type 'string' is not assignable to type 'number'.
year.value = '2020'

āĻ•āĻ–āύāĻ“ āĻ•āĻ–āύāĻ“ āφāĻŽāĻžāĻĻ⧇āϰ āĻāĻ•āϟāĻŋ āϰ⧇āĻĢ⧇āϰ āĻ…āĻ­ā§āϝāĻ¨ā§āϤāϰ⧀āĻŖ āĻŽāĻžāύ⧇āϰ āϜāĻ¨ā§āϝ āϜāϟāĻŋāϞ āĻĒā§āϰāĻ•āĻžāϰāϗ⧁āϞāĻŋ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻ•āϰāϤ⧇ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āφāĻŽāϰāĻž Ref āϟāĻžāχāĻĒ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻāϟāĻŋ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŋ:

ts
import { ref } from 'vue'
import type { Ref } from 'vue'

const year: Ref<string | number> = ref('2020')

year.value = 2020 // ok!

āĻ…āĻĨāĻŦāĻž, āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ…āύ⧁āĻŽāĻžāύāϕ⧇ āĻ“āĻ­āĻžāϰāϰāĻžāχāĻĄ āĻ•āϰāϤ⧇ ref() āĻ•āϞ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻĒāĻžāϏ āĻ•āϰ⧇:

ts
// resulting type: Ref<string | number>
const year = ref<string | number>('2020')

year.value = 2020 // ok!

āφāĻĒāύāĻŋ āϝāĻĻāĻŋ āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• āϟāĻžāχāĻĒ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻ•āϰ⧇āύ āĻ•āĻŋāĻ¨ā§āϤ⧁ āĻĒā§āϰāĻžāĻĨāĻŽāĻŋāĻ• āĻŽāĻžāύāϟāĻŋ āĻŦāĻžāĻĻ āĻĻ⧇āύ, āϤāĻžāĻšāϞ⧇ āĻĢāϞāĻžāĻĢāϞ⧇āϰ āϧāϰāύāϟāĻŋ āĻšāĻŦ⧇ āĻāĻ•āϟāĻŋ āχāωāύāĻŋāϝāĻŧāύ⧇āϰ āϧāϰāύ āϝāĻžāϤ⧇ undefined āĻ…āĻ¨ā§āϤāĻ°ā§āϭ⧁āĻ•ā§āϤ āĻĨāĻžāϕ⧇:

ts
// inferred type: Ref<number | undefined>
const n = ref<number>()

Typing reactive() ​

reactive() āĻāĻ›āĻžāĻĄāĻŧāĻžāĻ“ āĻāϰ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻĨ⧇āϕ⧇ āϟāĻžāχāĻĒāϟāĻŋāϕ⧇ āĻ…āĻ¨ā§āϤāĻ°ā§āύāĻŋāĻšāĻŋāϤāĻ­āĻžāĻŦ⧇ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰ⧇:

ts
import { reactive } from 'vue'

// inferred type: { title: string }
const book = reactive({ title: 'Vue 3 Guide' })

āĻ¸ā§āĻĒāĻˇā§āϟāĻ­āĻžāĻŦ⧇ āĻāĻ•āϟāĻŋ reactive āĻŦ⧈āĻļāĻŋāĻˇā§āĻŸā§āϝ āϟāĻžāχāĻĒ āĻ•āϰāϤ⧇, āφāĻŽāϰāĻž āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰāĻŋ:

ts
import { reactive } from 'vue'

interface Book {
  title: string
  year?: number
}

const book: Book = reactive({ title: 'Vue 3 Guide' })

TIP

āĻāϟāĻŋ reactive() āĻāϰ āĻœā§‡āύ⧇āϰāĻŋāĻ• āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāϰ āĻĒāϰāĻžāĻŽāĻ°ā§āĻļ āĻĻ⧇āĻ“āϝāĻŧāĻž āĻšāϝāĻŧ āύāĻž āĻ•āĻžāϰāĻŖ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻ•āϰāĻž āϟāĻžāχāĻĒ, āϝāĻž āύ⧇āĻ¸ā§āĻŸā§‡āĻĄ āϰ⧇āĻĢ āφāύāĻ°ā§āϝāĻžāĻĒāĻŋāĻ‚ āĻĒāϰāĻŋāϚāĻžāϞāύāĻž āĻ•āϰ⧇, āĻœā§‡āύ⧇āϰāĻŋāĻ• āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āϟāĻžāχāĻĒ āĻĨ⧇āϕ⧇ āφāϞāĻžāĻĻāĻžāĨ¤

Typing computed() ​

āĻĒā§āϰāĻžāĻĒā§āϤāĻŋāϰ āϰāĻŋāϟāĻžāĻ°ā§āύ āĻŽāĻžāύ⧇āϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ computed() āĻāϰ āϧāϰāύ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰ⧇:

ts
import { ref, computed } from 'vue'

const count = ref(0)

// inferred type: ComputedRef<number>
const double = computed(() => count.value * 2)

// => TS Error: Property 'split' does not exist on type 'number'
const result = double.value.split('')

āφāĻĒāύāĻŋ āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻāĻ•āϟāĻŋ āĻ¸ā§āĻĒāĻˇā§āϟ āĻĒā§āϰāĻ•āĻžāϰ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ:

ts
const double = computed<number>(() => {
  // type error if this doesn't return a number
})

Typing Event Handlers ​

āύ⧇āϟāĻŋāĻ­ DOM āχāϭ⧇āĻ¨ā§āϟāϗ⧁āϞāĻŋāϰ āϏāĻžāĻĨ⧇ āĻĄāĻŋāϞ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ, āφāĻŽāϰāĻž āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞāĻžāϰāϕ⧇ āϏāĻ āĻŋāĻ•āĻ­āĻžāĻŦ⧇ āϝ⧇ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻĻāĻŋāϝāĻŧ⧇ āĻĨāĻžāĻ•āĻŋ āϤāĻž āϟāĻžāχāĻĒ āĻ•āϰāĻž āĻ•āĻžāĻ°ā§āϝāĻ•āϰ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āφāϏ⧁āύ āĻāχ āωāĻĻāĻžāĻšāϰāĻŖāϟāĻŋ āĻāĻ•āĻŦāĻžāϰ āĻĻ⧇āϖ⧇ āύ⧇āĻ“āϝāĻŧāĻž āϝāĻžāĻ•:

vue
<script setup lang="ts">
function handleChange(event) {
  // `event` implicitly has `any` type
  console.log(event.target.value)
}
</script>

<template>
  <input type="text" @change="handleChange" />
</template>

āϟāĻžāχāĻĒ āĻŸā§€āĻ•āĻž āĻŦā§āϝāϤ⧀āϤ, event āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡ āύāĻŋāĻšāĻŋāϤāĻ­āĻžāĻŦ⧇ any āĻĒā§āϰāĻ•āĻžāϰ āĻĨāĻžāĻ•āĻŦ⧇āĨ¤ tsconfig.json-āĻ āϝāĻĻāĻŋ "strict": true āĻŦāĻž "noImplicitAny": true āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšāϝāĻŧ āϤāĻžāĻšāϞ⧇ āĻāϟāĻŋ āĻāĻ•āϟāĻŋ TS āĻ¤ā§āϰ⧁āϟāĻŋāϰ āĻ•āĻžāϰāĻŖ āĻšāĻŦ⧇āĨ¤ āϤāĻžāχ āχāϭ⧇āĻ¨ā§āϟ āĻšā§āϝāĻžāĻ¨ā§āĻĄāϞāĻžāϰāĻĻ⧇āϰ āϝ⧁āĻ•ā§āϤāĻŋ āĻ¸ā§āĻĒāĻˇā§āϟāĻ­āĻžāĻŦ⧇ āĻŸā§€āĻ•āĻž āĻ•āϰāĻžāϰ āϏ⧁āĻĒāĻžāϰāĻŋāĻļ āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ āωāĻĒāϰāĻ¨ā§āϤ⧁, event āĻāϰ āĻŦ⧈āĻļāĻŋāĻˇā§āĻŸā§āϝāϗ⧁āϞāĻŋ āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āφāĻĒāύāĻžāϕ⧇ āϟāĻžāχāĻĒ āĻ…ā§āϝāĻžāϏāĻžāĻ°ā§āĻŸā§‡āĻļāύ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϤ⧇ āĻšāϤ⧇ āĻĒāĻžāϰ⧇:

ts
function handleChange(event: Event) {
  console.log((event.target as HTMLInputElement).value)
}

Typing Provide / Inject ​

āĻĒā§āϰāĻĻāĻžāύ āĻāĻŦāĻ‚ āχāύāĻœā§‡āĻ•āĻļāύ āϏāĻžāϧāĻžāϰāĻŖāϤ āĻĒ⧃āĻĨāĻ• āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ āϏāĻžā§āϚāĻžāϞāĻŋāϤ āĻšāϝāĻŧ. āϏāĻ āĻŋāĻ•āĻ­āĻžāĻŦ⧇ āχāύāĻœā§‡āĻ•āĻļāύ āĻ•āϰāĻž āĻŽāĻžāύ āϟāĻžāχāĻĒ āĻ•āϰāϤ⧇, Vue āĻāĻ•āϟāĻŋ InjectionKey āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰ⧇, āϝāĻž āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• āϟāĻžāχāĻĒ āϝāĻž Symbol āĻĒā§āϰāϏāĻžāϰāĻŋāϤ āĻ•āϰ⧇āĨ¤ āĻāϟāĻŋ āĻĒā§āϰāĻĻāĻžāύāĻ•āĻžāϰ⧀ āĻāĻŦāĻ‚ āĻ­ā§‹āĻ•ā§āϤāĻžāϰ āĻŽāĻ§ā§āϝ⧇ āχāύāĻœā§‡āĻ•āĻļāύ⧇āϰ āĻŽāĻžāύāϟāĻŋāϰ āϧāϰāύ āϏāĻŋāĻ™ā§āĻ• āĻ•āϰāϤ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇:

ts
import { provide, inject } from 'vue'
import type { InjectionKey } from 'vue'

const key = Symbol() as InjectionKey<string>

provide(key, 'foo') // providing non-string value will result in error

const foo = inject(key) // type of foo: string | undefined

āĻāϟāĻŋ āĻāĻ•āϟāĻŋ āĻĒ⧃āĻĨāĻ• āĻĢāĻžāχāϞ⧇ āχāύāĻœā§‡āĻ•āĻļāύ āϕ⧀ āϰāĻžāĻ–āĻžāϰ āϏ⧁āĻĒāĻžāϰāĻŋāĻļ āĻ•āϰāĻž āĻšāϝāĻŧ āϝāĻžāϤ⧇ āĻāϟāĻŋ āĻāĻ•āĻžāϧāĻŋāĻ• āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āĻŸā§‡ āφāĻŽāĻĻāĻžāύāĻŋ āĻ•āϰāĻž āϝāĻžāϝāĻŧāĨ¤

āĻ¸ā§āĻŸā§āϰāĻŋāĻ‚ āχāύāĻœā§‡āĻ•āĻļāύ āϕ⧀ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ, āχāύāĻœā§‡āĻ•āĻļāύ⧇āϰ āĻŽāĻžāύāϟāĻŋāϰ āϧāϰāύ āĻšāĻŦ⧇ unknown, āĻāĻŦāĻ‚ āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• āϟāĻžāχāĻĒ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻ¸ā§āĻĒāĻˇā§āϟāĻ­āĻžāĻŦ⧇ āĻ˜ā§‹āώāĻŖāĻž āĻ•āϰāĻž āĻĒā§āϰāϝāĻŧā§‹āϜāύ:

ts
const foo = inject<string>('foo') // type: string | undefined

āϞāĻ•ā§āĻˇā§āϝ āĻ•āϰ⧁āύ āχāύāĻœā§‡āĻ•āĻļāύ⧇āϰ āĻŽāĻžāύāϟāĻŋ āĻāĻ–āύāĻ“ undefined āĻšāϤ⧇ āĻĒāĻžāϰ⧇, āĻ•āĻžāϰāĻŖ āϰāĻžāύāϟāĻžāχāĻŽā§‡ āϕ⧋āύ⧋ āĻĒā§āϰāĻĻāĻžāύāĻ•āĻžāϰ⧀ āĻāχ āĻŽāĻžāύāϟāĻŋ āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰāĻŦ⧇ āĻāĻŽāύ āϕ⧋āύ⧋ āĻ—ā§āϝāĻžāϰāĻžāĻ¨ā§āϟāĻŋ āύ⧇āχāĨ¤

āĻāĻ•āϟāĻŋ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻŽāĻžāύ āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰ⧇ undefined āĻĒā§āϰāĻ•āĻžāϰāϟāĻŋ āϏāϰāĻžāύ⧋ āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇:

ts
const foo = inject<string>('foo', 'bar') // type: string

āφāĻĒāύāĻŋ āϝāĻĻāĻŋ āύāĻŋāĻļā§āϚāĻŋāϤ āĻšāύ āϝ⧇ āĻŽāĻžāύāϟāĻŋ āϏāĻ°ā§āĻŦāĻĻāĻž āĻĒā§āϰāĻĻāĻžāύ āĻ•āϰāĻž āĻšāϝāĻŧ, āϤāĻžāĻšāϞ⧇ āφāĻĒāύāĻŋ āĻŽāĻžāύāϟāĻŋ āĻœā§‹āϰ āĻ•āϰ⧇ āύāĻŋāĻ•ā§āώ⧇āĻĒ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ:

ts
const foo = inject('foo') as string

Typing Template Refs ​

Vue 3.5 āĻāĻŦāĻ‚ @vue/language-tools 2.1 (IDE āĻ­āĻžāώāĻž āĻĒāϰāĻŋāώ⧇āĻŦāĻž āĻāĻŦāĻ‚ vue-tsc āωāĻ­āϝāĻŧāϕ⧇āχ āĻļāĻ•ā§āϤāĻŋ āĻĻ⧇āϝāĻŧ), SFC-āϤ⧇ useTemplateRef() āĻĻā§āĻŦāĻžāϰāĻž āϤ⧈āϰāĻŋ āϰ⧇āĻĢ⧇āϰ āϧāϰāύ āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇ āĻ¸ā§āĻŸā§āϝāĻžāϟāĻŋāĻ• refs āϕ⧋āύ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āĻŸā§‡ āĻŽāĻŋāϞāĻŋāϤ āϰ⧇āĻĢ āĻ…ā§āϝāĻžāĻŸā§āϰāĻŋāĻŦāĻŋāωāϟ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇ āϤāĻžāϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇āĨ¤

āϝ⧇ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇ āĻ¸ā§āĻŦāϝāĻŧāĻ‚-āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāĻž āϏāĻŽā§āĻ­āĻŦ āύāϝāĻŧ, āφāĻĒāύāĻŋ āĻāĻ–āύāĻ“ āĻœā§‡āύ⧇āϰāĻŋāĻ• āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻŸā§‡āĻŽāĻĒā§āϞ⧇āϟ āϰ⧇āĻĢāϟāĻŋāϕ⧇ āĻāĻ•āϟāĻŋ āϏ⧁āĻ¸ā§āĻĒāĻˇā§āϟ āĻĒā§āϰāĻ•āĻžāϰ⧇ āĻ•āĻžāĻ¸ā§āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύ:

ts
const el = useTemplateRef<HTMLInputElement>('el')
ā§Š.ā§Ģ āĻāϰ āφāϗ⧇ āĻŦā§āϝāĻŦāĻšāĻžāϰ

āĻŸā§‡āĻŽāĻĒā§āϞ⧇āϟ āϰ⧇āĻĢ āĻāĻ•āϟāĻŋ āĻ¸ā§āĻĒāĻˇā§āϟ āĻœā§‡āύ⧇āϰāĻŋāĻ• āϟāĻžāχāĻĒ āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āϟ āĻāĻŦāĻ‚ 'āύāĻžāϞ' āĻāϰ āĻāĻ•āϟāĻŋ āĻĒā§āϰāĻžāĻĨāĻŽāĻŋāĻ• āĻŽāĻžāύ āĻĻāĻŋāϝāĻŧ⧇ āϤ⧈āϰāĻŋ āĻ•āϰāĻž āωāϚāĻŋāϤ:

vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'

const el = ref<HTMLInputElement | null>(null)

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

<template>
  <input ref="el" />
</template>

āϏāĻ āĻŋāĻ• DOM āχāĻ¨ā§āϟāĻžāϰāĻĢ⧇āϏ āĻĒ⧇āϤ⧇ āφāĻĒāύāĻŋ MDN āĻāϰ āĻŽāϤ⧋ āĻĒ⧃āĻˇā§āĻ āĻžāϗ⧁āϞāĻŋ āĻĒāϰ⧀āĻ•ā§āώāĻž āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤

āĻŽāύ⧇ āϰāĻžāĻ–āĻŦ⧇āύ āϝ⧇ āĻ•āĻ ā§‹āϰ āϧāϰāύ⧇āϰ āύāĻŋāϰāĻžāĻĒāĻ¤ā§āϤāĻžāϰ āϜāĻ¨ā§āϝ, el.value āĻ…ā§āϝāĻžāĻ•ā§āϏ⧇āϏ āĻ•āϰāĻžāϰ āϏāĻŽāϝāĻŧ āϐāĻšā§āĻ›āĻŋāĻ• āĻšā§‡āχāύāĻŋāĻ‚ āĻŦāĻž āϟāĻžāχāĻĒ āĻ—āĻžāĻ°ā§āĻĄ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻĒā§āϰāϝāĻŧā§‹āϜāύāĨ¤ āĻ•āĻžāϰāĻŖ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ āĻŽāĻžāωāĻ¨ā§āϟ āύāĻž āĻšāĻ“āϝāĻŧāĻž āĻĒāĻ°ā§āϝāĻ¨ā§āϤ āϰ⧇āĻĢ āĻāϰ āĻŽāĻžāύ null āĻĨāĻžāϕ⧇ āĻāĻŦāĻ‚ āϝāĻĻāĻŋ āϰ⧇āĻĢāĻžāϰ⧇āĻ¨ā§āϏāĻ•ā§ƒāϤ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ v-if āĻĻā§āĻŦāĻžāϰāĻž āφāύāĻŽāĻžāωāĻ¨ā§āϟ āĻ•āϰāĻž āĻšāϝāĻŧ āϤāĻžāĻšāϞ⧇ āĻāϟāĻŋ null āϏ⧇āϟ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇āĨ¤

Typing Component Template Refs ​

Vue 3.5 āĻāĻŦāĻ‚ @vue/language-tools 2.1 (IDE āĻ­āĻžāώāĻž āĻĒāϰāĻŋāώ⧇āĻŦāĻž āĻāĻŦāĻ‚ vue-tsc āωāĻ­āϝāĻŧāϕ⧇āχ āĻļāĻ•ā§āϤāĻŋ āĻĻ⧇āϝāĻŧ), SFC-āϤ⧇ useTemplateRef() āĻĻā§āĻŦāĻžāϰāĻž āϤ⧈āϰāĻŋ āϰ⧇āĻĢ⧇āϰ āϧāϰāύ āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇ āĻ¸ā§āĻŸā§āϝāĻžāϟāĻŋāĻ• āϰ⧇āĻĢ⧇āϰ āωāĻĒāϰ āĻ­āĻŋāĻ¤ā§āϤāĻŋ āĻ•āϰ⧇ āϕ⧋āύ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ āĻŦāĻž āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āĻŸā§‡āϰ āϏāĻžāĻĨ⧇ āĻŽāĻŋāϞ⧇ āϝāĻžāĻ“āϝāĻŧāĻž āϰ⧇āĻĢ āĻ…ā§āϝāĻžāĻŸā§āϰāĻŋāĻŦāĻŋāωāϟ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇āĨ¤

āϝ⧇āϏāĻŦ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇ āĻ¸ā§āĻŦāϝāĻŧāĻ‚-āĻ…āύ⧁āĻŽāĻžāύ āĻ•āϰāĻž āϏāĻŽā§āĻ­āĻŦ āύāϝāĻŧ (āϝ⧇āĻŽāύ āύāύ-āĻāϏāĻāĻĢāϏāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻŦāĻž āĻ—āϤāĻŋāĻļā§€āϞ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ), āφāĻĒāύāĻŋ āĻāĻ–āύāĻ“ āĻœā§‡āύ⧇āϰāĻŋāĻ• āφāĻ°ā§āϗ⧁āĻŽā§‡āĻ¨ā§āĻŸā§‡āϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻŸā§‡āĻŽāĻĒā§āϞ⧇āϟ āϰ⧇āĻĢāϕ⧇ āĻāĻ•āϟāĻŋ āϏ⧁āĻ¸ā§āĻĒāĻˇā§āϟ āĻĒā§āϰāĻ•āĻžāϰ⧇ āĻ•āĻžāĻ¸ā§āϟ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇āύāĨ¤

āĻāĻ•āϟāĻŋ āχāĻŽā§āĻĒā§‹āĻ°ā§āϟ āĻ•āϰāĻž āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āĻŸā§‡āϰ āχāύāĻ¸ā§āĻŸā§āϝāĻžāĻ¨ā§āϏ āϟāĻžāχāĻĒ āĻĒ⧇āϤ⧇, āφāĻŽāĻžāĻĻ⧇āϰ āĻĒā§āϰāĻĨāĻŽā§‡ typeof āĻāϰ āĻŽāĻžāĻ§ā§āϝāĻŽā§‡ āĻāϰ āϟāĻžāχāĻĒ āĻĒ⧇āϤ⧇ āĻšāĻŦ⧇, āϤāĻžāϰāĻĒāϰ TypeScript āĻāϰ āĻ…āĻ¨ā§āϤāĻ°ā§āύāĻŋāĻ°ā§āĻŽāĻŋāϤ InstanceType āχāωāϟāĻŋāϞāĻŋāϟāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āĻāϰ āχāύāĻ¸ā§āĻŸā§āϝāĻžāĻ¨ā§āϏ āϟāĻžāχāĻĒ āĻŦ⧇āϰ āĻ•āϰāϤ⧇ āĻšāĻŦ⧇:

App.vue
vue
<script setup lang="ts">
import { useTemplateRef } from 'vue'
import Foo from './Foo.vue'
import Bar from './Bar.vue'

type FooType = InstanceType<typeof Foo>
type BarType = InstanceType<typeof Bar>

const compRef = useTemplateRef<FooType | BarType>('comp')
</script>

<template>
  <component :is="Math.random() > 0.5 ? Foo : Bar" ref="comp" />
</template>

āϝ⧇ āĻ•ā§āώ⧇āĻ¤ā§āϰ⧇ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āĻŸā§‡āϰ āϏāĻ āĻŋāĻ• āϧāϰāύ āĻĒāĻžāĻ“āϝāĻŧāĻž āϝāĻžāϝāĻŧ āύāĻž āĻŦāĻž āϗ⧁āϰ⧁āĻ¤ā§āĻŦāĻĒā§‚āĻ°ā§āĻŖ āύāϝāĻŧ, āϤāĻžāϰ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤ⧇ ComponentPublicInstance āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āϝ⧇āϤ⧇ āĻĒāĻžāϰ⧇āĨ¤ āĻāϟāĻŋ āϕ⧇āĻŦāϞāĻŽāĻžāĻ¤ā§āϰ āϏ⧇āχ āĻŦ⧈āĻļāĻŋāĻˇā§āĻŸā§āϝāϗ⧁āϞāĻŋ āĻ…āĻ¨ā§āϤāĻ°ā§āϭ⧁āĻ•ā§āϤ āĻ•āϰāĻŦ⧇ āϝāĻž āϏāĻŽāĻ¸ā§āϤ āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ āĻĻā§āĻŦāĻžāϰāĻž āĻ­āĻžāĻ— āĻ•āϰāĻž āĻšāϝāĻŧ, āϝ⧇āĻŽāύ $el:

ts
import { useTemplateRef } from 'vue'
import type { ComponentPublicInstance } from 'vue'

const child = useTemplateRef<ComponentPublicInstance>('child')

āωāĻ˛ā§āϞ⧇āĻ– āĻ•āϰāĻž āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟāϟāĻŋ āĻāĻ•āϟāĻŋ āĻœā§‡āύ⧇āϰāĻŋāĻ• āĻ•āĻŽā§āĻĒā§‹āύ⧇āĻ¨ā§āϟ, āωāĻĻāĻžāĻšāϰāĻŖāĻ¸ā§āĻŦāϰ⧂āĻĒ MyGenericModal:

MyGenericModal.vue
vue
<script setup lang="ts" generic="ContentType extends string | number">
import { ref } from 'vue'

const content = ref<ContentType | null>(null)

const open = (newContent: ContentType) => (content.value = newContent)

defineExpose({
  open
})
</script>

āĻāϟāĻŋāϕ⧇ vue-component-type-helpers āϞāĻžāχāĻŦā§āϰ⧇āϰāĻŋ āĻĨ⧇āϕ⧇ ComponentExposed āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰ⧇ āωāĻ˛ā§āϞ⧇āĻ– āĻ•āϰāĻž āĻĻāϰāĻ•āĻžāϰ āĻ•āĻžāϰāĻŖ InstanceType āĻ•āĻžāϜ āĻ•āϰāĻŦ⧇ āύāĻžāĨ¤

App.vue
vue
<script setup lang="ts">
import { useTemplateRef } from 'vue'
import MyGenericModal from './MyGenericModal.vue'
import type { ComponentExposed } from 'vue-component-type-helpers'

const modal = useTemplateRef<ComponentExposed<typeof MyGenericModal>>('modal')

const openModal = () => {
  modal.value?.open('newValue')
}
</script>

Note that with @vue/language-tools 2.1+, static template refs' types can be automatically inferred and the above is only needed in edge cases.

TypeScript with Composition API has loaded