πŸͺŸβœ–οΈπŸ”

Vue & TypeScript

Quick intro

  • Lucas Huang (he/him)
    • 🟦 LinkedIn ---- @superko
    • 🦊 GitLab ------ @superko
    • πŸ™ GitHub ------ @pt8o
    • 🦣 Mastodon ---- @lph@mastodon.social
    • πŸ“Έ Instagram --- @h5ien
    • πŸ“Έ Instagram --- @ragnar.rigatoni
  • senior FE @ Shopify
  • usually React + TypeScript dev
  • "self-taught"
  • πŸ—£οΈ

What is Vue?

  • πŸͺŸ frontend JS framework
  • ⏱️ performance
  • πŸŽ“ gentle learning curve

What we're doing

  • πŸ“ simple Vue project setup with TypeScript
  • 🧰 useful tools
  • πŸ•³οΈ common patterns & pitfalls
  • 🌢️ Vue vs. React hot takes (maybe)

What we're not doing

  • πŸŽ“ walkthrough/code-along
  • 🀿 technical deep dive

Let's get started

 vitejs.dev/guide

 vuejs.org/guide/typescript/overview.html

				    
					    npm create vite@latest
				    
			    
or
				    
				  	  npm create vite@latest live-demo --template vue-ts
			  	  
		  	  
or
				    
				  	  npm init vue@latest
			  	  
		  	  
then
            
              cd live-demo && npm install
			  	  
		  	  

VS Code

"First class" IDE

  • 🧩 Vue Language Features (Volar)
  • 🧩 TypeScript Vue Plugin (Volar)
  • ❌ TypeScript and JavaScript Language Features

❗ Vite does not type check

rely on IDE || run tsc --noEmit manually

            
              npm run dev
              #  VITE v4.3.4  ready in 468 ms
              #  ➜  Local:   http://localhost:5173/
              #  ➜  Network: use --host to expose
              #  ➜  press h to show help
            
          

Declaring props

            
              // !TS: props just passed in as an array
              <script setup>
                defineProps(['msg']);
              </script>

              
            
          
            
              // "type-based declaration"
              <script lang="ts" setup>
                defineProps<{
                  msg: string,
                }>();
              </script>
            
          
            
              // "runtime declaration"
              <script lang="ts" setup>
                defineProps({
                  msg: {
                    type: String,
                    required: true,
                  },
                });
              </script>
            
          

Default values

            
              // type-based
              withDefaults(defineProps<{
                msg?: string,
              }>(), {
                msg: "Default message",
              });
            
          
            
              // runtime
              defineProps({
                msg: {
                  type: String,
                  default: "Default message",
                },
              });
            
          

More complex prop shape

            
              // type-based
              withDefaults(defineProps<{
                messages: {
                  title: string,
                  body: string,
                }
              }>(), {
                messages: () => ({
                  title: "First message",
                  body: "Second message"
                }),
              });
            
          
            
              // runtime
              import { PropType } from "vue";

              defineProps({
                messages: {
                  type: Object as PropType<{
                    title: string,
                    body: string,
                  }>,
                  default: () => ({
                    title: "First message",
                    body: "Second message",
                  });
                },
              });
            
          

Using interfaces: type-based declaration

            
              // βœ…
              interface Props {
                messages: {
                  title: string,
                  body: string,
                },
              };

              withDefaults(defineProps<Props>(), {
                messages: () => ({
                  title: "First message",
                  body: "Second message"
                }),
              });
            
          
            
              // ❌ (for now)
              import type { Props } from "./types";

              withDefaults(defineProps<Props>(), {
                messages: () => ({
                  title: "First message",
                  body: "Second message"
                }),
              });
            
          

Using interfaces: runtime declaration

            
              // ❌
              import type { Props } from "./types";

              defineProps({ // ???
            
          
            
              // types.ts
              // ❌
              export interface Props {
                messages: {
                  title: string,
                  body: string,
                },
              };

              // βœ…
              export interface MessageObject {
                title: string,
                body: string,
              }
            
          
            
              import { PropType } from "vue";
              import type { MessageObject } from "./types";

              defineProps({
                messages: {
                  type: Object as PropType<MessageObject>,
                  default: () => ({
                    title: "First message",
                    body: "Second message"
                  }),
                },
              });
            
          

Using interfaces: type-based declaration (reprise)

            
              import type { MessageObject } from "./types";

              withDefaults(defineProps<{
                messages: MessageObject,
              }>(), {
                messages: () => ({
                  title: "First message",
                  body: "Second message"
                }),
              });
            
          

emit, ref, computed, oh my!

            
              import { ref } from 'vue'

              // inferred type: Ref<number>
              const year = ref(2023);
              year.value = "2023"; // ❌

              const year = ref<number | string>(2023);
              year.value = "2023"; // βœ…
            
          

 vuejs.org/guide/typescript/composition-api.html

Vue context: provide/inject

            
              // ContextProvider.vue
              provide("count", 0);
            
          
            
              // Component.vue
              

              
            
          
            
              // Component.vue
              const count = inject("count") as number; // πŸ€”
            
          

Typing provide/inject πŸ”

            
              // context.ts
              import { InjectionKey } from "vue";

              export const countKey = Symbol("count") as InjectionKey<number>;
            
          

            
              // ContextProvider.vue
              import {countKey} from "./context";

              provide(countKey, 0);
            
          
            
              // Component.vue
              import {countKey} from "./context";

              const count = inject(countKey);
            
          

Summary

  • quick intro to Vue
  • project setup with Vite
  • VSCode recommendations
  • defineProps: type-based vs. runtime declaration
  • very quick mention of ref typing
  • provide/inject typing

Homework

  • Vue Options vs. Composition API
  • <script setup> vs. non-setup workflow
  • emit, computed, reactive
  • reactivity in general
  • lifecycle hooks

?

🌢️🌢️🌢️