조건
- 빌더 메소드로 프로퍼티를 하나씩 쌓아가면서,
한 번 호출한 메소드는 타입에서 제거하여 다시 호출 못하도록 제약 추가
설치
npm install @sinclair/typebox
Code
import { Static, TSchema, Type } from "@sinclair/typebox";
import * as Util from "util";
// ========================================================================
// Helper
// ========================================================================
class ControllerHelper {
public static InjectRequiredFlags(obj: TSchema): void {
if(obj["properties"] === undefined) return;
const reqs = obj["required"];
reqs?.forEach((prop: any) => {
obj["properties"][prop]["required"] = true;
})
}
}
// ========================================================================
// Types
// ========================================================================
type HttpRequestMethod = "get" | "post" | "put" | "patch" | "delete";
type ControllerOptions = {
requireAuth: boolean;
}
type MixBuilder<
TQuery = any,
TParam = any,
TBody = any,
TMethods extends string = ""
> = Omit<ControllerBuilder<TQuery, TParam, TBody, TMethods>, TMethods>;
// ========================================================================
// Dto Examples
// ========================================================================
type UserFindQueryDto = Static<typeof UserFindQueryDto>;
const UserFindQueryDto = Type.Object({
id: Type.Integer(),
name: Type.Optional(Type.String())
});
type UserFindParamDto = Static<typeof UserFindParamDto>;
const UserFindParamDto = Type.Object({
id: Type.Integer(),
nick: Type.Optional(Type.String())
});
// ========================================================================
// Class
// ========================================================================
class ControllerBuilder <
TQuery = any,
TParam = any,
TBody = any,
Tx extends string = ""
> {
private url: string;
private method: HttpRequestMethod;
private summaryDesc: string;
private detailDesc: string;
private options: ControllerOptions = {} as any;
public query: TQuery;
public param: TParam;
public body : TBody;
public static Begin() {
return new ControllerBuilder();
}
public Auth() : MixBuilder<TQuery, TParam, TBody, Tx | "Auth">
{
this.options.requireAuth = true;
return this as any;
}
public URL(url: string) : MixBuilder<TQuery, TParam, TBody, Tx | "URL">
{
this.url = url;
return this as any;
}
public Method(method: HttpRequestMethod) : MixBuilder<TQuery, TParam, TBody, Tx | "Method">
{
this.method = method;
return this as any;
}
public Query<TQuery extends TSchema>(query: TQuery)
: MixBuilder<Static<TQuery>, TParam, TBody, Tx | "Query">
{
ControllerHelper.InjectRequiredFlags(query);
(this as any).query = query;
return (this as any);
}
public Param<TParam extends TSchema>(param: TParam)
: MixBuilder<TQuery, Static<TParam>, TBody, Tx | "Param">
{
ControllerHelper.InjectRequiredFlags(param);
(this as any).param = param;
return (this as any);
}
public Body<TBody extends TSchema>(body: TBody)
: MixBuilder<TQuery, TParam, Static<TBody>, Tx | "Body">
{
ControllerHelper.InjectRequiredFlags(body);
(this as any).body = body;
return (this as any);
}
// TODO: 컨트롤러 구현 바디 작성
public Test(q: (a: {
query: TQuery,
param: TParam,
}) => void) {
return this; // TODO: 제거
}
}
// ========================================================================
// Usage
// ========================================================================
const userFind =
ControllerBuilder.Begin()
.Auth()
.Method("get")
.URL("/")
.Query(UserFindQueryDto)
.Param(UserFindParamDto)
.Test(
(obj) => {
obj.query.id;
obj.param.nick;
}
)
;
// console.log(userFind);
console.log(Util.inspect(userFind, true, null, true))
userFind.query.id;