Express는 웹 및 모바일 애플리케이션을 위한 강력한 기능 세트를 제공하는 최소한의 유연한 Node.js 웹 애플리케이션 프레임워크입니다.
TypeScript Express는 DT 라이브러리로 되어 있습니다.
npm install --save @types/express
Memo:
최신 Express 버전에는 bodyParser가 내장이 되어 있습니다. 따로 설치를 할 필요가 없습니다.
Express 내장 모듈은 export = e
로 commonJS
로 되어 있습니다. 때문에 Module Export에 E 변수명으로 되어 있어도 변수명을 사용자 임의로 정할 수 있습니다.
Express 내부 구조는 다음과 같습니다.
declare function e(): core.Express;
declare namespace e {
/**
* This is a built-in middleware function in Express. It parses incoming requests with JSON payloads and is based on body-parser.
* @since 4.16.0
*/
var json: typeof bodyParser.json;
...
}
Express는 express-serve-static-core 내부에 다양한 기능을 내장하고 있습니다.
import * as core from "express-serve-static-core";
내부 구조는 Node.js의 구조와 유사하며, http 모듈을 기반으로 리퀘스트와 리스폰스를 확장한 정도로 이루어져 있습니다.
덕분에 Express를 사용함으로써 보다 쉽게 웹 애플리케이션을 구축할 수 있습니다.
declare global {
namespace Express {
// These open interfaces may be extended in an application-specific manner via declaration merging.
// See for example method-override.d.ts (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/method-override/index.d.ts)
interface Request {}
interface Response {}
interface Locals {}
interface Application {}
}
}
Declare Global를 만든 이유는 내부에서 내용을 수정 할 수 있습니다.
Express 미들웨어는 오버로딩으로 되어 있습니다.
export interface IRouterMatcher<
T,
Method extends "all" | "get" | "post" | "put" | "delete" | "patch" | "options" | "head" = any,
> {
<
Route extends string,
P = RouteParameters<Route>,
ResBody = any,
ReqBody = any,
ReqQuery = ParsedQs,
LocalsObj extends Record<string, any> = Record<string, any>,
>(
// (it's used as the default type parameter for P)
path: Route,
// (This generic is meant to be passed explicitly.)
...handlers: Array<RequestHandler<P, ResBody, ReqBody, ReqQuery, LocalsObj>>
): T;
<
Path extends string,
P = RouteParameters<Path>,
ResBody = any,
ReqBody = any,
ReqQuery = ParsedQs,
LocalsObj extends Record<string, any> = Record<string, any>,
>(
// (it's used as the default type parameter for P)
path: Path,
// (This generic is meant to be passed explicitly.)
...handlers: Array<RequestHandlerParams<P, ResBody, ReqBody, ReqQuery, LocalsObj>>
): T;
....
handlers는 배열 타입으로 되어 있습니다. 그리고 RequestHandler는 Express 미들웨어입니다.
export interface RequestHandler<
P = ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = ParsedQs,
LocalsObj extends Record<string, any> = Record<string, any>,
> {
// tslint:disable-next-line callable-types (This is extended from and can't extend from a type alias in ts<2.2)
(
req: Request<P, ResBody, ReqBody, ReqQuery, LocalsObj>,
res: Response<ResBody, LocalsObj>,
next: NextFunction,
): void;
}
RequestHandler 구조는 req, res, next 익숙한 구조로 되어 있습니다.
때문에 Middleware 타입은 RequestHandler 타입입니다.
const middleware: RequestHandler<
{ paramType: string },
{ message: string },
{ bodyType: number },
{ queryType: boolean },
{ localType: unknown }
> = (req, res, next) => {
req.params.paramType,
req.body.bodyType;
req.query.queryType;
res.locals.localType;
res.json({
message: 'hello',
})
};
declare
는 TypeScript에서 전역 객체에 새로운 속성이나 타입을 추가할 때 사용하는 구문입니다. 일반적으로 TypeScript는 전역 스코프에서 새로운 변수나 속성을 추가할 때 컴파일러가 해당 식별자를 인식하지 못하고 오류를 발생시키는데, 이를 방지하기 위해 declare global을 사용할 수 있습니다.
declare
는 기능이 많습니다. declare
를 단순히 선언 할 수도 있고 NameSpace, Global, Module 등 확장으로 사용 할 수도 있습니다.
declare global {
namespace Express {
// These open interfaces may be extended in an application-specific manner via declaration merging.
// See for example method-override.d.ts (https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/method-override/index.d.ts)
interface Request {}
interface Response {}
interface Locals {}
interface Application {}
}
}
errorMiddleware를 생성하겠습니다.
const errorMiddleware: ErrorRequestHandler = (err: Error, req, res, next) => {
console.log(err.status)
}
ErrorRequestHandler의 내부구조는 다음과 같습니다.
export type ErrorRequestHandler<
P = ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = ParsedQs,
LocalsObj extends Record<string, any> = Record<string, any>,
> = (
err: any,
req: Request<P, ResBody, ReqBody, ReqQuery, LocalsObj>,
res: Response<ResBody, LocalsObj>,
next: NextFunction,
) => void;
err는 any 타입으로 Error 객체를 사용합니다. 그렇지만 err.status가 인식이 되지 않아 에러가 발생합니다.
이 경우에 interface를 정의합니다.
interface Error {
status: string
}
err.status
를 인식합니다. 이 Error를 전역에서 사용하려고 global로 지정하겠습니다.
declare global {
interface Error {
status: string
}
}
export {}
index.d.ts 파일을 생성해서 global로 인식하려면 export를 해야만 Error 객체를 인식 할 수 있습니다.