Vue中option的类型推断

如果大家有用ts写代码,会发现当咱们写组件的option(选项)时,可能很好的提供类型推断,当然前提是你要应用Vue.extend()办法。

具体的应用大家能够参考我写的这篇博客,如何在vue中不借助vue-class-decorator实现ts类型推断。

vue2中应用ts

那vue的类型是如何实现在参数中为this提供类型推断呢?

以下这段代码在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显示的推断类型,咱们能够采取传参的形式

interface Context {    $injected: string}function bar(this: Context, a: number) {    this.$injected // 这样是能工作的}

然而,如果咱们传入Record参数类型(索引对象),这样就会有问题,它(ts)并不能很好提供类型校验了

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) // okmethods.double('foo') // errormethods.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 togetherfunction 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(上下文类型)的原理了