最近在使用 koa-router
和 typescript 编写 Handler 时,遇到了一个奇怪的问题
import { Context } from 'koa';
import Router from 'koa-router';
const router = new Router();
router.get('/', (ctx: Context) => {
});
此时 typescript
会报错
TS2769: No overload matches this call.
Overload 1 of 4, '(name: string, path: string | RegExp, ...middleware: Middleware<ParameterizedContext<any, IRouterParamContext<any, {}>>>[]): Router<...>', gave the following error.
Argument of type '(ctx: Context) => void' is not assignable to parameter of type 'string | RegExp'.
Type '(ctx: Context) => void' is missing the following properties from type 'RegExp': exec, test, source, global, and 12 more.
Overload 2 of 4, '(path: string | RegExp | (string | RegExp)[], ...middleware: Middleware<ParameterizedContext<any, IRouterParamContext<any, {}>>>[]): Router<...>', gave the following error.
Argument of type '(ctx: Context) => void' is not assignable to parameter of type 'Middleware<ParameterizedContext<any, IRouterParamContext<any, {}>>>'.
Types of parameters 'ctx' and 'context' are incompatible.
Property 'websocket' is missing in type 'ExtendableContext & { state: any; } & IRouterParamContext<any, {}>' but required in type 'Context'.
一大串的很不好看懂对吧,大部分教程都是上面那种写法,为什么会出现这种报错呢?
问题其实出在最后一行,
Property 'websocket' is missing in type 'ExtendableContext & { state: any; } & IRouterParamContext<any, {}>' but required in type 'Context'.
因为在这个项目中我用了 koa-websocket
和 @types/koa-websocket
而在 @types/koa-websocket/index.d.ts
中
declare module "koa" {
interface Context {
websocket: ws;
path: string;
}
}
这和@types/koa-router
中
export type RouterContext<StateT = any, CustomT = {}> =
Koa.ParameterizedContext<StateT, CustomT & IRouterParamContext<StateT, CustomT>>;
// For backward compatibility IRouterContext needs to be an interface
// But it's deprecated - please use `RouterContext` instead
export interface IRouterContext extends RouterContext {}
export type IMiddleware<StateT = any, CustomT = {}> =
Koa.Middleware<StateT, CustomT & IRouterParamContext<StateT, CustomT>>
export interface IParamMiddleware<STateT = any, CustomT = {}> {
(param: string, ctx: RouterContext<STateT, CustomT>, next: () => Promise<any>): any;
}
的中相冲突,这段太长,其实转换后的ctx为
context: (ExtendableContext & {state: StateT} & CustomT & IRouterParamContext<StateT, CustomT>)
那么如何修复呢?
一个方法是将 Context
替换为 ParameterizedContext
router.get('/', (ctx: ParameterizedContext) => {})
但是很快你就会遇到另一个问题,使用 ParameterizedContext
你就无法使用 ctx.websocket
,因为 Context
实际上是继承自 ParameterizedContext
。
@types/koa/index.d.ts
type ParameterizedContext<StateT = DefaultState, CustomT = DefaultContext> = ExtendableContext & {
state: StateT;
} & CustomT;
interface Context extends ParameterizedContext {}
最后的修复方案是
import { Context, DefaultState} from 'koa';
const router = new Router<DefaultState, Context>();
router.get('/', (ctx: Context) => {})
即修改了前面 Koa-router 中的 CustomT
,这样既不会报错,也可以正常使用websocket
。
除了koa-websocket
,其他的如koa-session
也会修改 Context
,出现上述问题,也可以用同样的方式修复。
如果你需要 ctx.state
上的补全(比如使用 koa-jwt
,它会修改 ctx.state.user
),也可以将上面的 DefaultState
修改为你所需要的类型。
今天也是和 typescript 斗智斗勇的一天呢 :)
Referer: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/36161
最后一次更新于2021-02-25
0 条评论