typescript如何在Vue提供this類型推斷

Vue中option的類型推斷

若是你們有用ts寫代碼,會發現當咱們寫組件的option(選項)時,可以很好的提供類型推斷,固然前提是你要使用Vue.extend()方法。javascript

具體的使用你們能夠參考我寫的這篇博客,如何在vue中不借助vue-class-decorator實現ts類型推斷。vue

vue2中使用tsjava

那vue的類型是如何實如今參數中爲this提供類型推斷呢?segmentfault

如下這段代碼在javascript可以很好的工做,也很容易理解。可是當咱們切換到ts作靜態類型檢查時,this並不能很好的工做,那咱們如何讓this可以提供類型推斷呢?函數

export default {
    data: {
        first_name: "Anthony",
        last_name: "Fu",
    },
    computed: {
        full_name() {
            return this.first_name + " " + this.last_name;
        },
    },
    methods: {
        hi() {
            alert(this.full_name);
        },
    },
};

this提供類型

爲了能讓this顯示的推斷類型,咱們能夠採起傳參的方式this

interface Context {
    $injected: string
}
function bar(this: Context, a: number) {
    this.$injected // 這樣是能工做的
}

可是,若是咱們傳入Record參數類型(索引對象),這樣就會有問題,它(ts)並不能很好提供類型校驗了code

type Methods = Record<string, (this: Context, ...args:any[]) => any>
const methods: Methods = {
    bar(a: number) {
        this.$injected // ok
    }
}
methods.bar('foo', 'bar') // 沒有提示錯誤,由於參數類型已經變爲 `any[]`

並且也不能總是讓用戶,提供參數類型吧!這種體驗是很是不友好的,因此爲了實現類型校驗,咱們須要尋找另外一種方法了。對象

ThisType

在瞭解了vue的代碼以後,發現了ts一個頗有用的內置類型 -ThisType索引

ThisType定義:經過 ThisType咱們能夠在對象字面量中鍵入 this,並提供經過上下文類型控制 this類型的便捷方式。它只有在 --noImplicitThis的選項下才有效

ThisType能夠影響全部的嵌套函數,那咱們能夠這樣寫了接口

type Methods = {
    double: (a: number) => number
    deep: {
         nested: {
            half: (a: number) => number
         }
    }
}

const methods: Methods & ThisType<Methods & Context> = {
    double(a: number) {
        this.$injected // ok
        return a * 2
    },
    deep: {
        nested: {
            half(a: number) {
                this.$injected // ok
                return a / 2
            }
        }
    }
}

methods.double(2) // ok
methods.double('foo') // error
methods.deep.nested.half(4) // ok

能夠看到this的類型推斷已經生效了,可是有個缺點仍是須要用戶去定義方法的接口,那咱們能不能自動推斷類型呢?

實現define

能夠的,經過函數來自動推斷類型。

type Options<T> = {
 methods?: T 
} & ThisType<T & Context>

function define<T>(options: Options<T>) {
    return options
}

define({
    methods: {
        foo() {
            this.$injected // ok
        },
    },
})

方法已經能自動推斷了,那麼接下來,咱們能夠接着實現computeddata的類型推斷

整個完整的demo以下:

/* ---- Type ---- */

export type ExtractComputedReturns<T extends any> = {
[key in keyof T]: T[key] extends (...args: any[]) => infer TReturn
 ? TReturn
 : never
}

type Options<D = {}, C = {}, M = {}> = {
    data: () => D
    computed: C
    methods: M
    mounted: () => void
    // and other options
} 

& ThisType<D & M & ExtractComputedReturns<C>> // merge them together

function define<D, C, M>(options: Options<D, C, M>) {}

/* ---- Usage ---- */
define({
    data() {
        return {
            first_name: "Anthony",
            last_name: "Fu",
        }
    },
    computed: {
        fullname() {
           return this.first_name + " " + this.last_name
        },
    },
    methods: {
        notify(msg: string) {
            alert(msg)
        }
    },
    mounted() {
        this.notify(this.fullname)
    },
})

其實define的原理就是Vue.extend能推斷this(上下文類型)的原理了

相關文章
相關標籤/搜索