ProcDecorator 정리
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
import { LoginCtx } from "./ReqLogin";
|
||||
import type { ProcDecoratorGenerator } from "./base";
|
||||
import type { Request, Response } from "express";
|
||||
|
||||
|
||||
export interface UserLevelCtx {
|
||||
userLevel: number;
|
||||
}
|
||||
|
||||
export function ParseUserLevel<Q extends LoginCtx>(): ProcDecoratorGenerator<UserLevelCtx, Q> {
|
||||
return (inCtx) => {
|
||||
return {
|
||||
userLevel: 123,
|
||||
...inCtx,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { LoginCtx } from "./ReqLogin";
|
||||
import type { ProcDecoratorGenerator } from "./base";
|
||||
import type { Request, Response } from "express";
|
||||
|
||||
export type GameLoginCtx = {
|
||||
generalID: number;
|
||||
}
|
||||
|
||||
export function ReqGameLogin<Q extends LoginCtx>(): ProcDecoratorGenerator<GameLoginCtx, Q> {
|
||||
return (inCtx) => {
|
||||
return {
|
||||
generalID: inCtx.userID * 4,
|
||||
...inCtx,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { Empty, ProcDecoratorPrePostGenerator } from "./base";
|
||||
|
||||
export type LoginCtx = {
|
||||
userID: number;
|
||||
}
|
||||
|
||||
export function ReqLogin<Q extends object = Empty>(): ProcDecoratorPrePostGenerator<LoginCtx, Q> {
|
||||
return [
|
||||
(inCtx) => {
|
||||
return {
|
||||
userID: 999,
|
||||
...inCtx,
|
||||
};
|
||||
},
|
||||
(inCtx) => {
|
||||
console.log('ReqLogin post');
|
||||
return inCtx;
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
import type { Request, Response } from "express";
|
||||
|
||||
type MayBePromise<T> = T | Promise<T>;
|
||||
|
||||
export type InvalidProc = {
|
||||
invalidProcSymbol: unknown;
|
||||
reason?: string;
|
||||
alreadyProcessed?: boolean;
|
||||
}
|
||||
|
||||
export interface ProcDecorator<Out, In extends object = Record<string, never>> {
|
||||
(inCtx: In, req: Request, res: Response): MayBePromise<Out | InvalidProc>;
|
||||
}
|
||||
|
||||
export type Empty = Record<string, never>;
|
||||
|
||||
type Login = {
|
||||
userID: number;
|
||||
}
|
||||
type GameLogin = {
|
||||
userID: number;
|
||||
generalID: number;
|
||||
}
|
||||
type UserLevel = {
|
||||
userID: number;
|
||||
userLevel: number;
|
||||
}
|
||||
|
||||
export function ParseUserLevel<Q extends Login = Login>(): ProcDecorator<Q & UserLevel, Q> {
|
||||
return (inCtx) => {
|
||||
return {
|
||||
userLevel: 123,
|
||||
...inCtx,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function ReqLogin<Q extends object = Empty>(): [ProcDecorator<Q & Login, Q>, ProcDecorator<Login & object, Login & object>] {
|
||||
return [
|
||||
(inCtx) => {
|
||||
return {
|
||||
userID: 999,
|
||||
...inCtx,
|
||||
};
|
||||
},
|
||||
(inCtx) => {
|
||||
//무언가
|
||||
return inCtx;
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export function ReqGameLogin<Q extends Login = Login>(): ProcDecorator<Q & GameLogin, Q> {
|
||||
return (inCtx) => {
|
||||
return {
|
||||
generalID: inCtx.userID * 4,
|
||||
...inCtx,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export type ProcDecoratorGenerator<B extends object, A extends object> = ProcDecorator<B & A, A>;
|
||||
export type ProcDecoratorPrePostGenerator<B extends object, A extends object> = [ProcDecorator<B & A, A>, ProcDecorator<B & object, B & object>];
|
||||
|
||||
type Compose2<B extends object, A extends object, D, C> = D & B extends A & C ? B extends C ? ProcDecorator<D & B, A> : never : never;
|
||||
|
||||
type PD1<B extends object, A extends object> = () => ProcDecorator<B, A>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type PD2<B extends object, A extends object> = () => [ProcDecorator<B, A>, ProcDecorator<any, any>];
|
||||
|
||||
type PackChain<T> =
|
||||
T extends readonly [] ? ProcDecorator<Empty, Empty> :
|
||||
T extends readonly [PD1<infer B, infer A>] ? ProcDecorator<B, A> :
|
||||
T extends readonly [PD2<infer B, infer A>] ? ProcDecorator<B, A> :
|
||||
T extends readonly [PD1<infer B, infer A>, PD1<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
||||
T extends readonly [PD2<infer B, infer A>, PD1<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
||||
T extends readonly [PD1<infer B, infer A>, PD2<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
||||
T extends readonly [PD2<infer B, infer A>, PD2<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
||||
never;
|
||||
|
||||
type Resolve<T> = T extends ProcDecorator<infer B, infer A> ? Empty extends A ? B : never : never;
|
||||
|
||||
|
||||
export type ResolveChain<T extends ProcDecoratorChain> = Resolve<PackChain<T>>;
|
||||
|
||||
const q1 = [
|
||||
ReqLogin,
|
||||
ParseUserLevel,
|
||||
ReqGameLogin,
|
||||
] as const;
|
||||
|
||||
const q2 = [
|
||||
ReqLogin,
|
||||
ParseUserLevel,
|
||||
] as const;
|
||||
|
||||
|
||||
type R1 = Resolve<PackChain<typeof q1>>;
|
||||
type R2 = Resolve<PackChain<typeof q2>>;
|
||||
const r1: R1 = undefined as unknown as R1;
|
||||
const r2: R2 = undefined as unknown as R2;
|
||||
r1.userLevel;
|
||||
r2.userID;
|
||||
|
||||
|
||||
async function test(req: Request, res: Response) {
|
||||
const a0 = await ReqLogin()[0]({}, req, res);
|
||||
if ('invalidProcSymbol' in a0) {
|
||||
return false;
|
||||
}
|
||||
const a1 = await ParseUserLevel()(a0, req, res);
|
||||
if ('invalidProcSymbol' in a1) {
|
||||
return false;
|
||||
}
|
||||
const a2 = await ReqGameLogin()(a1, req, res);
|
||||
if ('invalidProcSymbol' in a2) {
|
||||
return false;
|
||||
}
|
||||
const b0 = await ReqLogin()[1](a2, req, res);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ProcDecoratorChain = readonly ((() => ProcDecorator<any, any>) | (() => [ProcDecorator<any, any>, ProcDecorator<any, any>]))[];
|
||||
|
||||
function unpack2<A extends ProcDecorator<any, any>, B>(v: A | [A, B]): A {
|
||||
if (Array.isArray(v)) {
|
||||
return v[0];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
function test2<T extends ProcDecoratorChain>(req: Request, res: Response, chain: T): Resolve<PackChain<T>> | false {
|
||||
const tmp = chain[0]();
|
||||
|
||||
const actors = chain.map((v) => unpack2(v()));
|
||||
|
||||
let result = {};
|
||||
for (const actor of actors) {
|
||||
result = actor(result, req, res);
|
||||
if ('isInvalid' in result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return result as Resolve<PackChain<T>>;
|
||||
}
|
||||
|
||||
const finalResult1 = test2({} as Request, {} as Response, q1);
|
||||
if (finalResult1 !== false) {
|
||||
finalResult1.userLevel;
|
||||
}
|
||||
Reference in New Issue
Block a user