乐趣区

关于typescript:typescript如何在Vue提供this类型推断

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) // 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(上下文类型) 的原理了

退出移动版