Commit 6d652bb9 authored by nanahira's avatar nanahira

better type restriction

parent a1855d8d
...@@ -9,7 +9,10 @@ import { ...@@ -9,7 +9,10 @@ import {
KoishiCommandPutDef, KoishiCommandPutDef,
KoishiOnContextScope, KoishiOnContextScope,
OnContextFunction, OnContextFunction,
PickEventFunction,
PluginDefinition,
TopLevelActionDef, TopLevelActionDef,
TypedMethodDecorator,
} from '../def'; } from '../def';
import 'reflect-metadata'; import 'reflect-metadata';
import { import {
...@@ -20,6 +23,8 @@ import { ...@@ -20,6 +23,8 @@ import {
Selection, Selection,
Session, Session,
I18n, I18n,
Awaitable,
Middleware,
} from 'koishi'; } from 'koishi';
import { Metadata } from '../meta/metadata.decorators'; import { Metadata } from '../meta/metadata.decorators';
import { CommandPut, DoRegister } from '../registry'; import { CommandPut, DoRegister } from '../registry';
...@@ -28,32 +33,43 @@ import { ...@@ -28,32 +33,43 @@ import {
applyOptionToCommand, applyOptionToCommand,
registerTemplate, registerTemplate,
} from '../utility'; } from '../utility';
import { EventMap, BeforeEventMap } from 'koishi';
// Register method // Register method
export const UseMiddleware = (prepend = false): MethodDecorator => export const UseMiddleware = (prepend = false) =>
DoRegister.middleware(prepend); DoRegister.middleware(prepend);
export const UseEvent = (name: EventName, prepend = false): MethodDecorator => export const UseEvent = <K extends EventName>(
name: K,
prepend = false,
): TypedMethodDecorator<PickEventFunction<EventMap, K>> =>
DoRegister.event({ name, prepend }); DoRegister.event({ name, prepend });
export const UseBeforeEvent = ( export const UseBeforeEvent = <K extends BeforeEventName>(
name: BeforeEventName, name: K,
prepend = false, prepend = false,
): MethodDecorator => DoRegister.beforeEvent({ name, prepend }); ): TypedMethodDecorator<PickEventFunction<BeforeEventMap, K>> =>
export const UsePlugin = (): MethodDecorator => DoRegister.plugin(); DoRegister.beforeEvent({ name, prepend });
export const UsePlugin = () => DoRegister.plugin();
export function UseCommand<D extends string>( export function UseCommand<D extends string>(
def: D, def: D,
config?: CommandConfigExtended, config?: CommandConfigExtended,
): MethodDecorator; ): TypedMethodDecorator<
(...args: any[]) => Awaitable<string | void | undefined>
>;
export function UseCommand<D extends string>( export function UseCommand<D extends string>(
def: D, def: D,
desc: string, desc: string,
config?: CommandConfigExtended, config?: CommandConfigExtended,
): MethodDecorator; ): TypedMethodDecorator<
(...args: any[]) => Awaitable<string | void | undefined>
>;
export function UseCommand( export function UseCommand(
def: string, def: string,
...args: [CommandConfigExtended?] | [string, CommandConfigExtended?] ...args: [CommandConfigExtended?] | [string, CommandConfigExtended?]
): MethodDecorator { ): TypedMethodDecorator<
(...args: any[]) => Awaitable<string | void | undefined>
> {
const desc = typeof args[0] === 'string' ? (args.shift() as string) : ''; const desc = typeof args[0] === 'string' ? (args.shift() as string) : '';
const config = args[0] as CommandConfigExtended; const config = args[0] as CommandConfigExtended;
return (obj, key: string, des) => { return (obj, key: string, des) => {
......
...@@ -7,6 +7,7 @@ import { ...@@ -7,6 +7,7 @@ import {
Plugin, Plugin,
Selection, Selection,
I18n, I18n,
Awaitable,
} from 'koishi'; } from 'koishi';
import type { DefaultContext, DefaultState, ParameterizedContext } from 'koa'; import type { DefaultContext, DefaultState, ParameterizedContext } from 'koa';
import type { RouterParamContext } from '@koa/router'; import type { RouterParamContext } from '@koa/router';
...@@ -176,3 +177,29 @@ export interface CommandArgDef { ...@@ -176,3 +177,29 @@ export interface CommandArgDef {
} }
export type ParamRenderer = <T>(v: T) => T; export type ParamRenderer = <T>(v: T) => T;
export type TypedMethodDecorator<F extends (...args: any[]) => any> = <
T extends F,
>(
// eslint-disable-next-line @typescript-eslint/ban-types
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>,
) => void;
export type FunctionParam<F extends (...args: any[]) => any> = F extends (
...args: infer R
) => any
? R
: never;
export type FunctionReturn<F extends (...args: any[]) => any> = F extends (
...args: any[]
) => infer R
? R
: never;
export type PickEventFunction<M, K extends keyof M> = M[K] extends (
...args: any[]
) => any
? (...args: FunctionParam<M[K]>) => Awaitable<FunctionReturn<M[K]>>
: M[K];
import { MethodMap, MethodRegistry } from '../abstract-registry'; import { MethodMap, MethodRegistry } from '../abstract-registry';
import { Argv, Awaitable, Context, MaybeArray } from 'koishi';
import { import {
Argv,
Awaitable,
BeforeEventMap,
Context,
EventMap,
I18n,
MaybeArray,
Middleware,
} from 'koishi';
import {
BeforeEventName,
BeforeEventNameAndPrepend, BeforeEventNameAndPrepend,
CommandRegisterConfig, CommandRegisterConfig,
EventName,
EventNameAndPrepend, EventNameAndPrepend,
KoaContext,
KoishiCommandDefinition, KoishiCommandDefinition,
KoishiDoRegister, KoishiDoRegister,
KoishiDoRegisterKeys, KoishiDoRegisterKeys,
KoishiRouteDef, KoishiRouteDef,
MappingStruct, MappingStruct,
PickEventFunction,
PluginDefinition, PluginDefinition,
TypedMethodDecorator,
} from '../../def'; } from '../../def';
import { Metadata } from '../../meta/metadata.decorators'; import { Metadata } from '../../meta/metadata.decorators';
import { reflector } from '../../meta/meta-fetch'; import { reflector } from '../../meta/meta-fetch';
import { CommandPut } from './command-put'; import { CommandPut } from './command-put';
import { applySelector, generateRenderer, renderObject } from '../../utility'; import { applySelector, generateRenderer } from '../../utility';
import { Next as KoaNext } from 'koa';
import { IncomingMessage } from 'http';
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
export namespace DoRegister { export namespace DoRegister {
export function decorate<K extends keyof ConfigMap>( export function decorate<K extends keyof ConfigMap>(
config: Config<K>, config: Config<K>,
): MethodDecorator { ): K extends keyof DecoratorMap
? TypedMethodDecorator<DecoratorMap[K]>
: MethodDecorator {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return Metadata.set(KoishiDoRegister, config, KoishiDoRegisterKeys); return Metadata.set(KoishiDoRegister, config, KoishiDoRegisterKeys);
} }
...@@ -44,7 +64,8 @@ export namespace DoRegister { ...@@ -44,7 +64,8 @@ export namespace DoRegister {
method: MethodMap<ConfigMap, any, [Context, any, string, ...any[]]>[K], method: MethodMap<ConfigMap, any, [Context, any, string, ...any[]]>[K],
) { ) {
this.extend(name, method); this.extend(name, method);
return (config: ConfigMap[K]) => decorate({ type: name, data: config }); return (config: ConfigMap[K]) =>
decorate<K>({ type: name, data: config });
} }
} }
...@@ -61,6 +82,18 @@ export namespace DoRegister { ...@@ -61,6 +82,18 @@ export namespace DoRegister {
interval: number; interval: number;
} }
export interface DecoratorMap {
middleware: Middleware;
onEvent: PickEventFunction<EventMap, EventName>;
beforeEvent: PickEventFunction<BeforeEventMap, BeforeEventName>;
plugin(): Awaitable<PluginDefinition | undefined>;
command(...args: any[]): Awaitable<string | void | undefined>;
route(ctx: KoaContext, Next: KoaNext): any;
ws(socket: WebSocket, request: IncomingMessage): any;
formatter: I18n.Formatter;
preset: I18n.Renderer;
}
export type Config<K extends keyof ConfigMap = keyof ConfigMap> = export type Config<K extends keyof ConfigMap = keyof ConfigMap> =
MappingStruct<ConfigMap, K>; MappingStruct<ConfigMap, K>;
......
...@@ -26,13 +26,13 @@ class MyClass { ...@@ -26,13 +26,13 @@ class MyClass {
if (session.content === 'ping') { if (session.content === 'ping') {
return 'pong'; return 'pong';
} }
return next; return next();
} }
@UseEvent('message') @UseEvent('message')
async onMessage(session: Session) { async onMessage(session: Session) {
if (session.content === 'pang') { if (session.content === 'pang') {
return session.send('peng'); await session.send('peng');
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment