vue typescript

Gary Ng
12 min readMay 17, 2021

--

Basic type 資料型態

  1. string
  2. number
  3. Array
  4. tuple
let x : [string, number]
x = ['a', 1] // ok
x = [1, 'a'] // error

5. enum

// enum 預設從 0 開始
enum Color {
Red,
Green,
Blue
}
let c : Color = Color.Red

6. void

7. object

先安裝 vue-property-decorator

npm i vue-property-decorator

class-based-component

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component({
name: 'PageComponent'
})
export default class PageComponent extends Vue { }</script>

等同於

<script>    export default {
name: 'PageComponent'
}</script>

Import the other component

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import PageComponent from '@/components/PageComponent.vue'
@Component({
name: 'PageComponent', components: { PageComponent
}
})
export default class PageComponent extends Vue { }</script>

等同於

<script>
import PageComponent from '@/components/PageComponent.vue'
export default {
name: 'PageComponent',
components: {
PageComponent
}
}</script>

Vuex

npm install vuex-module-decorators
npm install vuex-class

/store/index.js

// store/index.ts
import { Store } from 'vuex'
export const store = new Store({})
// store/atuh.ts
import { VuexModule, Action, Mutation, Module } from 'vuex-module-decorators'import { store } from '@/store' // 引入上面的 store@Module({ name: 'auth', stateFactory: true, namespaced: true, // namespaced dynamic: true, // 自動加載 store,})export default class AuthModule extends VuexModule {
// 顯示 action 的 error
@Action({ rawError: true }) async postLogin(payload: IPostLogin): Promise<void> { $cookies.set('auth', payload.access_token) // context 有基本的 commit dispatch 等等 this.context.dispatch('me/setMeData', null, { root: true }) } @Action({ rawError: true }) nuxtClientInit() { if ($cookies.getOriginal('auth')) { this.postLogin({ access_token: $cookies.getOriginal('auth') }) } }}

在 component 使用 vuex

import { Component, Vue, namespace } from 'nuxt-property-decorator'const auth = namespace('auth')@Componentexport default class LoginPage extends Vue {    @auth.Action('postLogin')    private postLogin!: (payload: IPostLogin) => void
@auth.Getter('getMe')
private getMe!: IUser}

Component data

<script lang="ts">import { Component, Vue, namespace } from 'nuxt-property-decorator'@Component({layout: 'auth',})
export default class Page extends Vue {
private authForm: AuthForm = { account: '', password: '', }}</script>

等同於

<script>export default {   name: 'Page',   data() {
return {
account: '',
password: '',
}
}}</script>

Component Props

import { Component, Vue, Prop } from 'nuxt-property-decorator'
export default class Page extends Vue { @Prop({ required: true, type: Array, default: [] })
readonly breadcrumbs!: Breadcrumbs[]
}

等同於

export default {    name: 'Page',    props: {
breadcrumbs: {
type: Array, default: () => [],
}

}
}

computed

get isMenus(): boolean {    return this.menu.menus.length > 0}

等同於

computed: {   isMenus() {      ///  
}
}

methods

toggleOpen(): void {    this.open = !this.open}

等同於

methods: {   toggleOpen() {      this.open = !this.open
}
}

watcher — 監聽某個變數的變化

import { Vue, Component, Watch } from 'vue-property-decorator'@Watch('child')
onChildChanged(val: string, oldVal: string) {}

等同於

watch: {    child(newValue, oldValue): {    }}

watch 監聽深層的物件

import { Vue, Component, Watch } from 'vue-property-decorator'@Watch('child', {   deep: false,
immediate: false
})
onChildChanged(val: string, oldVal: string) {}

等同於

watch: {  child: {    deep: false,    immediate: false,    handler(value: string, oldValue: string) {}
}
}

倘若 axios 有 async awit

import { AxiosResponse } from 'axios'
import { ICompanyAttr } from '@/interface/company'
async created() { const {data: company } : AxiosResponse<ICompanyAttr> = await apiGetData()
}

宣告 generic function

function hello<T>(params: T): T {
}

這樣做的話 hello 的 params 參數即可接受任何型別的資料

TypeScript plugin

import Cookies from 'js-cookie'
import Vue from 'vue'
// 定義 nuxt pluginimport { Plugin } from '@nuxt/types'
import { ICookiesMethod } from '@/interface/cookie'
// 定義 cookie method 的 interface
interface ICookiesMethod {
set: Function
get: Function
getOriginal: Function
remove: Function
removeAll: Function
removeCrossDomain: Function
}
// 將 $cookies 定義在 Vue interface 裡
// 這樣才能使用 this.$cookies
// 否則會編輯器會警告有錯
declare module 'vue/types/vue' {
interface Vue {
$cookies: ICookiesMethod
}
interface Context {
$cookies: ICookiesMethod
}
}
// 定義 cookies plugin
const cookies: Plugin = (context, inject) => {
const cookiesObj: ICookiesMethod = {
set: (name: string, value = {}, expires = { expires: 365 }) => {
Cookies.set(name, value, expires)
},
get: (name: string) => {
const val = Cookies.get(name)
try {
return JSON.parse(val)
} catch (err) {
return undefined
}
},
getOriginal: (name: string) => {
try {
return Cookies.get(name)
} catch (err) {
return undefined
}
},
remove: (name: string) => {
Cookies.remove(name)
},
removeCrossDomain: (name: string, attributes = { path: '/', domain: process.env.COOKIES_DOMAIN }) => {
Cookies.remove(name, attributes)
},
removeAll: () => {
const NodeList = Object.keys(Cookies.get())
NodeList.forEach(el => Cookies.remove(el))
window.location.replace('/')
},
}
inject('cookies', cookiesObj)
}
export default cookies

middleware 定義

/**
Middleware ==> data type
Context => context 的型別
**/
import { Middleware, Context } from '@nuxt/types'
// 以下例子為 ssr, 倘若是 spa 則不會有 server side 的問題// 定義驗證的 middlware
const authenticate: Middleware = async (context: Context) => {
if (!context.app.$cookiz.get('auth')) {
context.redirect('/login')
} else {
try {
await context.store.dispatch('me/setMeData')
} catch (e) {
context.redirect('/login')
}
}
}
export default authenticate

有些 plugin 會將資料掛載在 vue instance

// 例如
// vue-notification 套件
// 在 template 使用如下this.$notify()// 但是 typescript 除了引入 plugin 外// 在 tsconfig.json 要特別在 types: 設定
// 否則 this 吃不到 notify
// 以配下配供參考
{
"compilerOptions": {
"forceConsistentCasingInFileNames": false,
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "Node",
"lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noEmit": true,
"experimentalDecorators": true, // 使其可以使用 decorator
"baseUrl": ".",
"paths": { // path 轉換
"~/*": ["./*"],
"@/*": ["./*"]
},
// 設定在 instance, 這樣 this 才吃得到
"types": ["@nuxt/types", "@types/node", "@nuxtjs/axios", "vue-notification", "js-cookie"]
},
"exclude": ["node_modules", ".nuxt", "dist"]
}

額外補充

倘若使用 typescript 的話, 許多套件需安裝 @types 開頭的套件, 例如 lodash

// typescript 倘若只安裝以下套件
// 在 template 引用則會出錯
npm install --save lodash
// typescript 需安裝
npm install --save @types/lodash

例外也可以照以下的文件使用 vuex

https://github.com/championswimmer/vuex-module-decorators#accessing-modules-with-nuxtjs

--

--

Gary Ng
Gary Ng

Written by Gary Ng

軟體工程師、後端工程師

No responses yet