-
[Composition API] Typescript 적용하기 (script setup)Frontend/Vue3 2023. 3. 12. 16:34
Typing Component Props
<script setup lang="ts"> import { defineProps } from 'vue'; interface Props { name: string count?: number } const props = defineProps<Props>() </script>
Props는 상위 컴포넌트(부모)에서 하위 컴포넌트(자식)로 데이터를 넘겨줄 때 사용합니다.
Props Default Values
<script setup lang="ts"> import { defineProps, withDefaults } from 'vue'; interface Props { msg?: string labels?: string[] } const props = withDefaults(defineProps<Props>(), { msg: 'hello', labels: () => ['one', 'two'] }) </script>
default props값을 지정하는 방법입니다.
Typing Component Emits
<script setup lang="ts"> // runtime const emit = defineEmits(['change', 'update']) // type-based const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void }>() </script>
Emits는 하위 컴포넌트(자식)에서 상위 컴포넌트(부모)로 이벤트, 데이터를 넘겨줄 때 사용합니다.
Typing ref()
ref는 초기값으로 타입을 추론하는 기능이 기본적으로 내장되어 있습니다.
import { ref } from 'vue' // inferred type: Ref<number> const year = ref(2023) // => TS Error: Type 'string' is not assignable to type 'number'. year.value = '2023'
명시적인 타입 선언은 다음과 같습니다.
//1. type Ref 선언해 적용하는 방식 import { ref } from 'vue' import type { Ref } from 'vue' const year: Ref<string | number> = ref('2023') year.value = 2023 // ok! //2. ref() 에 override 하는 방식 // resulting type: Ref<string | number> const year = ref<string | number>('2023') year.value = 2023 // ok! --------------------------------------------- // 초기값을 지정하지 않으면 type에 undefined가 자동으로 포함 const n = ref<number>()
Typing reactive()
reactive도 초기값으로 타입을 추론하는 기능이 내장되어 있습니다.
import { reactive } from 'vue' // inferred type: { title: string } const book = reactive({ title: 'Vue 3 Guide' })
명시적인 타입 선언은 다음과 같습니다.
import { reactive } from 'vue' interface Book { title: string year?: number } const book: Book = reactive({ title: 'Vue 3 Guide' })
Typing computed()
computed는 getter의 리턴값을 기준으로 타입을 추론하는 기능이 내장되어 있습니다.
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('')
명시적인 타입 선언은 다음과 같습니다.
const double = computed<number>(() => { // type error if this doesn't return a number })
Typing Event Handlers
type을 명시하지 않으면 event의 인수는 any 타입을 갖습니다.
<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>
하지만 tsconfig.json에서 “strict”: true 또는 “noImplicitAny”: true가 사용되는 경우에는 타입 오류가 발생합니다.
따라서 다음과 같이 명시적으로 타입을 선언해줍시다.
function handleChange(event: Event) { console.log((event.target as HTMLInputElement).value) }
Typing Provide, Inject
Provide 와 Inject는 일반적으로 독립적인 컴포넌트로부터 수행됩니다.
Inject된 값을 올바르게 입력하기 위해 Vue는 Symbol을 확장하는 interface 유형인 InjectionKey interface를 제공합니다. 이는 Inject된 값과 provider 간에 타입을 동기화 하는데 사용할 수 있습니다.
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
여러 컴포넌트에서 사용할 수 있도록 Injection key를 별도의 파일에 배치하는 것이 좋습니다.
string Injection key를 사용하는 경우, Inject 값 유형은 unknown이 되기 때문에 generic type을 통해 명시적으로 선언해야 합니다.
const foo = inject<string>('foo') // type: string | undefined
Inject 값은 아직 undefined가 될 수 있습니다. 왜냐하면 provider가 런타임에 undefined를 제공하지 않는다는 보장이 없기 때문입니다.
다음과 같이 두번째 인자로 default value를 입력하면 undefined를 제거할 수 있습니다.
const foo = inject<string>('foo', 'bar') // type: string
값이 항상 제공된다고 확신하는 경우 값을 강제로 캐스팅할 수도 있습니다.
const foo = inject('foo') as string
Typing Template Refs
Template refs는 명시적인 generic 타입과 함께 초기값 null로 만들어야 합니다.
<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>
엄격한 타입 체크를 위해 el.value에 접근할 때 optional chaining 또는 type guard를 사용해야 합니다.
컴포넌트가 마운트되기 전까지는 초기 ref 값이 null이고, 참조된 element가 v-if에 의해 마운트 해제된 경우에도 null로 설정될 수 있기 때문입니다.
Typing Component Template Refs
자식 컴포넌트에 포함된 public method를 사용하기 위해 template ref를 선언해야 하는 경우가 있습니다. 예를 들어, 아래와 같이 모달을 여는 메서드가 있는 MyModal 자식 컴포넌트가 있습니다.
<!-- MyModal.vue --> <script setup lang="ts"> import { ref } from 'vue' const isContentShown = ref(false) const open = () => (isContentShown.value = true) defineExpose({ open }) </script>
MyModal의 인스턴스 type을 가져오려면 먼저 typeof를 통해 type을 가져온 다음 TS의 내장 InstanceType 유틸리티를 사용해 인스턴스 type을 추출해야 합니다.
<!-- App.vue --> <script setup lang="ts"> import MyModal from './MyModal.vue' const modal = ref<InstanceType<typeof MyModal> | null>(null) const openModal = () => { modal.value?.open() } </script>
https://vuejs.org/guide/typescript/composition-api.html#typing-component-props
TypeScript with Composition API | Vue.js
vuejs.org
'Frontend > Vue3' 카테고리의 다른 글
[Vue3] 배경지식 (0) 2023.05.01 [Composition API] Vue3 Slots 사용하기(script setup) (0) 2022.12.31 [Composition API] Vue3 Emit 사용하기(script setup) (0) 2022.12.31 [Composition API] Vue3 Props 사용하기(script setup) (0) 2022.12.25 Vue3 프로젝트 시작하기 (1) 2022.12.24