After half a year in the front end, the technology was very weak, and the work intensity of the state-owned enterprise was not high, so I spent a lot of time fishing, so I could make some small toys. In order to comprehensively improve and improve my development ability during fishing, I finally planned to try to build a low-code crawler platform, and the model was SELECTED as Koa + React + TS.

When I first learned koA, it was too light and amazing for someone who had written embedded software. It was so easy to get the service up and running with just a line of New koa ().listen(4396). Still feel some uncomfortable place, then want to improve it, so came up with this immature idea, we rational, friendly discussion 🤗🤗🤗

⚠️⚠️⚠️ This article has a certain title party suspicion, but it doesn’t matter for the sake of drainage

1. The types of koa.context. body and fetch.body.json()

When koA is used to write the back end, koa.context.body is the return body of the HTTP response, and the data returned can be set by assigning to it. When used with TS, the type is unknown:

//@types/ the type definition of Contex in the index.d.ts file of KOA
type ParameterizedContext<StateT = DefaultState, ContextT = DefaultContext, ResponseBodyT = unknown> = ExtendableContext
    & { state: StateT; }
    & ContextT
    & { body: ResponseBodyT; response: { body: ResponseBodyT }; };

interface Context extends ParameterizedContext {}
Copy the code

ParameterizedContext (ParameterizedContext) ParameterizedContext (ParameterizedContext)

While fetch/axios is used for HTTP requestsfetch.body.json()Gets the return body, when used with ts, of typePromise<any>Received with await type any, classical AnyScript:

These two “pits” (not pits, just bad to write) were extremely inconsistent with my expectations of an elegant TS development experience, so I had the idea to revamp it, add a type definition, and even discard swagger/ Apifox by sharing TS files. So I can continue to use TS + Koa + fetch to write elegant interfaces. In lightweight applications where both the front and back ends use TS, the efficiency of the front and back end interfaces is greatly improved.

I really like you 🥵! For you, I want to create type 😭😭😭!!

(For a quick mention, not the focus of this article 😋)

type & interface

For the most part, type and interface are used the same way: Interfaces vs Types in TypeScript. They can all be used for type definition/extension:

interface Api1 {
    code: number.message: string,}interface TimeApi1{
    timeStamp: Date;
}
type Api2 = {
    code: number.message:string,}type TimeApi2 = Api1 & {timeStamp: Date} // The extension is written slightly differently than interface

// They can even extend to each other
interface IntApi extends Api2{
    data: number
}
type TypeApi = Api1 & {data: number}
Copy the code

The difference is that Type has more quirks (type gymnastics) and interface can merge declarations

Interface Api1 {code: number,} interface Api1 {message: string,} Number, message: string,} type Api2 = {code: number} type Api2 = {message: string} // At this point, just like js, Api2 is assigned the following valueCopy the code

3.Koa, I really like you 🥵! For you, I want to expand the type 😭😭😭!!

before

Now you can start making specific type declarations for your project’s interface. The first is the Api.d.ts file that can be shared by the front and back ends. To unify and standardize interfaces, the front and back ends use this file uniformly:

namespace Api { 
  const enum Code { // Enumerates status codes
    success = 1,
    fail = 2,
    otherError = 3
  }
  
  type BaseApi = { // Base class of the interface from which the rest of the interface can be extended
    code: Code,
    message: string.data: object,}interface LoginApi extends BaseApi { //login
    data: {
      token: string.dueTime: Date,}}}export default Api
Copy the code

With the above files, when fetch/ AXIos is used on the front end, you can get clear type prompts for different interfaces:

// Because it is GET, type extension does not require particularly complicated operations

//fetch:
const resp = await fetch(url, opt);
const data: LoginApi = await resp.json();

//axios:
const resp: AxiosResponse<Api.LoginApi> = await axios(url)
// This is a simple extension, and a similar approach can be used to encapsulate interceptors for more complete functionality and type constraints
Copy the code

after

When the backend responds to the request, it shares this Api file with the front-end project, but the package’s built-in types need to be extended to accept the Api types we declare.

In Koa, decalre pairs are used firstKoa.contextThere are a lot of quirks in the use of declare here: The official documentation states that declare must be placed in a.d.ts file, and then it can be used globally. In theory, this is the case, but in practice, if you place code extending koA types in a.d.ts file, tsLint in the VSC will not report an error, but whenNPM embellish startAfter, the program will report an error:Node_modules has a higher declaration priority and overwrites d.ts. SRC at runtime. Node_modules has a higher declaration priority than d.ts. After many attempts, it was found that theapp.tsType extension with declare at the top of the file.

import Koa from 'koa' //import koa to type extend it
declare module 'koa' { 
  //@types/koa's index.d.ts file already defines context, so it is possible to extend context directly without extends
  interface Context<T = Api.BaseApi> {  // Only Body is used here, so only the type of context. Body is extended
    body: T
  }
  // Of course, the Context has so many built-in objects that you can add other objects to it if you want to extend it
}
Copy the code

With the type extension now live, we can move even faster:

/ / login middleware
async function login(ctx: Koa.Context<Api.LoginApi>, next: Koa.Next){
    / /... An operation
    ctx.body = {    This way, you can see the type hint for the Api when using ctx.body
        code: Api.Code.success,
        message: 'success'.data: {}... }}Copy the code

An abrupt end

In this way, we can bind and unify the interface types by sharing TS files.

Such a wonderful interface, please cheer for me, cheer for me Hehe, OK? I’m going to cherish TypeScript, and I’m going to cherish it