一起来为koa2中间件写definitions吧
发布于 4 年前 作者 hellopao 4227 次浏览 最后一次编辑是 3 年前 来自 分享

前阵子koa2发布了,一些中间件也增加了对koa2的支持,这当然是大大的好事了。但是,像我这样喜欢用typescript+koa2node的人来说,某个中间件没对应的.d.ts文件会是件很蛋疼的事。

没人写只能自己来了,写过之后会发现其实很简单,还能对那些中间件有更深入的了解。下面介绍下怎么找到支持koa2的中间件以及怎么写对应的.d.ts文件:

middlewares

koa的中间件可以在koawiki上看到,里面列出了哪些中间件支持koa2,我们直接在上面找就行了。

为了方便,我写了个命令行工具koa2-middlewares来查看这些中间件(当然是用typescript写的啦)。全局安装后,通过命令行koams list可以查看所有的koa2中间件, koams list -i会过滤掉已经有.d.ts文件的中间件。具体用法参见该工具的README

definitions

知道有哪些koa2中间件了就可以挑一个开始写了。以koa-compress为例:

通过koams open koa-compress命令打开koa-compressgithub主页,切到v2.x分支,可以看到它的用法如下:

var compress = require('koa-compress')
var Koa = require('koa')

var app = new Koa()
app.use(compress({
  filter: function (content_type) {
    return /text/i.test(content_type)
  },
  threshold: 2048,
  flush: require('zlib').Z_SYNC_FLUSH
}))

从用法上看知道koa-compress这个模块导出的是一个函数,接受一个options参数,options有三个属性,所以单从readme上的代码示例来看,koa-compress.d.ts的架子就是这样的:

/// <reference path="../node/node.d.ts" />
/// <reference path="../koa/koa.d.ts" />

declare module "koa-compress" {

    import * as Koa from "koa";

    function compress(options: {
        filter: (content_type: string) => boolean;
        threshold: number;
        flush:number;
    })

    export = compress;
}

我们再看下koa-compress的源码:

'use strict';

/**
 * Module dependencies.
 */

var compressible = require('compressible')
var isJSON = require('koa-is-json')
var status = require('statuses')
var Stream = require('stream')
var bytes = require('bytes')
var zlib = require('zlib')

/**
 * Encoding methods supported.
 */

var encodingMethods = {
  gzip: zlib.createGzip,
  deflate: zlib.createDeflate
}

/**
 * Compress middleware.
 *
 * @param {Object} [options]
 * @return {Function}
 * @api public
 */

module.exports = (options) => {
  options = options || {}

  var filter = options.filter || compressible

  var threshold = !options.threshold ? 1024
    : typeof options.threshold === 'number' ? options.threshold
    : typeof options.threshold === 'string' ? bytes(options.threshold)
    : 1024

  return function compress(ctx, next) {
    ctx.vary('Accept-Encoding')

    return next().then(() => {
      var body = ctx.body
      if (!body) return
      if (ctx.compress === false) return
      if (ctx.request.method === 'HEAD') return
      if (status.empty[ctx.response.status]) return
      if (ctx.response.get('Content-Encoding')) return

      // forced compression or implied
      if (!(ctx.compress === true || filter(ctx.response.type))) return

      // identity
      var encoding = ctx.acceptsEncodings('gzip', 'deflate', 'identity')
      if (!encoding) ctx.throw(406, 'supported encodings: gzip, deflate, identity')
      if (encoding === 'identity') return

      // json
      if (isJSON(body)) body = ctx.body = JSON.stringify(body)

      // threshold
      if (threshold && ctx.response.length < threshold) return

      ctx.set('Content-Encoding', encoding)
      ctx.res.removeHeader('Content-Length')

      var stream =
      ctx.body = encodingMethods[encoding](options)

      if (body instanceof Stream) {
        body.pipe(stream)
      } else {
        stream.end(body)
      }
    });
  };
}

代码一共就76行,通过源码我们可以得出以下结论:

  1. options为非必须参数

  2. options.filteroptions.threshold都为非必须属性

  3. options参数最后传给了zlib模块的方法,所以这个options是继承于zlib.ZlibOptions

  4. koa-compress导出的函数执行后返回一个函数,这个函数是可以作为koa实例的use方法的参数

所以往koa-compress.d.ts中填内容后会是下面这个样子:

/// <reference path="../node/node.d.ts" />
/// <reference path="../koa/koa.d.ts" />

declare module "koa-compress" {

    import * as Koa from "koa";
    import * as zlib from "zlib";

    interface ICompressOptions extends zlib.ZlibOptions {

        filter?: (content_type: string) => boolean;

        threshold?: number
    }
    
    function compress(options?: ICompressOptions): { (ctx: Koa.Context, next?: () => any): any };

    export = compress;
}

到这里koa-compress.d.ts基本就算是写好了,再加点注释什么的就可以用得很爽了,最终版本可以看下koa-compress.d.ts

写一个.d.ts文件就是这么简单,写完我们还要发布出去给别人用。具体步骤如下:

  1. forkDefinitelyTyped这个仓库到你的github

  2. 在自己fork过来的DefinitelyTyped添加对应中间件目录

  3. 目录里放对应.d.ts文件和-tests.ts文件。

  4. 提交代码,发起pull request,等待合并。

结语

编写和发布一个.d.ts文件就是这么简单。目前我已经添加了koa-routerkoa-statickoa-bodyparserkoa-favicon

喜欢typescript+koa2的童鞋可以一起来搞,方便自己也方便大家。

2 回复

能想办法通用么?类似于convert这样的

感觉用typescript最头痛的就是写typing

回到顶部