javascript - 如何向实际上是类的接口的原型添加函数?
将函数添加到一个值的原型上很容易,例如:
Array.prototype.removeAt = function (index) {
if (index >= 0 && index < this.length) {
this.splice(index, 1);
}
return this;
};
declare global {
export interface Array<T> {
removeAt(index: number): Array<T>;
}
}
但是当我试图向接口的原型添加函数时(它实际上是一个类,所以它有原型),它不再起作用。例如:
import { Router, RouteRecord } from 'vue-router';
Router.prototype.getRouteByName = function (this: Router, name: string): RouteRecord | undefined {
return this.getRoutes().find(m => m.name === name);
};
declare global {
export interface Router {
getRouteByName(name: string): RouteRecord | undefined;
}
}
当我编译它时出现错误:'Router' 仅指类型,但这里被用作值。
然而,类型 'Router' 被导出为一个接口 而实际上它在后端的 JavaScript 代码中是一个类,所以我认为一定有办法绕过这个限制并修改它的原型。
以下是 Router 的导出方式:
export interface Router {
// so many members, please see the detains on
// https://github.com/vuejs/router/blob/main/packages/router/src/router.ts#L189
}
我应该做什么来实现它?
答案#1
Router 在这里是一个接口,不是类函数,而且更重要的是,路由实例只是一个普通对象,所以你不能在这里扩展任何暴露的原型,你的代码甚至无法运行:
const router = { // returned from createRouter
currentRoute,
listening: true,
addRoute,
removeRoute,
clearRoutes: matcher.clearRoutes,
hasRoute,
getRoutes,
resolve,
options,
push,
replace,
go,
...
只需以任何你喜欢的方式重写 createRouter(),这里我使用一个类:
import { createRouter as createVueRouter, createWebHistory } from 'vue-router';
interface MyRouter extends ReturnType<typeof createVueRouter> { };
class MyRouter implements MyRouter {
getRouteByName(name: string) {
return this.getRoutes().find(m => m.name === name);
};
}
export function createRouter(...params: Parameters<typeof createVueRouter>): MyRouter {
const router = createVueRouter(...params);
Object.setPrototypeOf(router, MyRouter.prototype);
return router as MyRouter;
}
const router = createRouter({
history: createWebHistory(), routes: [{
path: '/',
name: 'test',
component: () => import('./test.vue')
}]
});
const route = router.getRouteByName('test');
所以导入这个函数并使用它来替代原来的 createRouter
答案#2
你不是通过 declare global 扩展这个,而是
import type { Router, RouteRecord } from 'vue-router';
// I'm not sure which one you need from these two.
import 'vue-router';
declare module 'vue-router' {
// not sure if you need the export keyword here
export interface Router {
getRouteByName(name: string): RouteRecord |
undefined;
}
}
你要么需要在想要使用 getRouteByName 的地方导入这个文件,要么必须将其添加为你的 tsconfig 的全局类型声明文件。(typeRoots, files,...不确定)
顺便说一下,路由器实际上是一个普通的 JS 对象,所以给它做原型扩展可能并不太有用,只需创建你自己的路由器插件或路由器工厂函数,比如包装 createRouter 的 createRouter。
