前阵子 koa2 发布了,一些中间件也增加了对 koa2 的支持,这当然是大大的好事了。但是,像我这样喜欢用 typescript + koa2 写 node 的人来说,某个中间件没对应的 .d.ts 文件会是件很蛋疼的事。
没人写只能自己来了,写过之后会发现其实很简单,还能对那些中间件有更深入的了解。下面介绍下怎么找到支持 koa2 的中间件以及怎么写对应的 .d.ts 文件:
koa 的中间件可以在 koa 的 wiki 上看到,里面列出了哪些中间件支持 koa2 ,我们直接在上面找就行了。
为了方便,我写了个命令行工具 koa2-middlewares 来查看这些中间件(当然是用 typescript 写的啦)。全局安装后,通过命令行 koams list 可以查看所有的 koa2 中间件,
koams list -i 会过滤掉已经有 .d.ts 文件的中间件。具体用法参见该工具的 README 。
知道有哪些 koa2 中间件了就可以挑一个开始写了。以 koa-compress 为例:
通过 koams open koa-compress 命令打开 koa-compress 的 github主页 ,切到 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行,通过源码我们可以得出以下结论:
options 为非必须参数
options.filter 和 options.threshold 都为非必须属性
options 参数最后传给了 zlib 模块的方法,所以这个 options 是继承于 zlib.ZlibOptions 的
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 文件就是这么简单,写完我们还要发布出去给别人用。具体步骤如下:
fork DefinitelyTyped 这个仓库到你的 github
在自己 fork 过来的 DefinitelyTyped 添加对应中间件目录
目录里放对应 .d.ts 文件和 -tests.ts 文件。
提交代码,发起 pull request ,等待合并。
编写和发布一个 .d.ts 文件就是这么简单。目前我已经添加了 koa-router 、 koa-static 、 koa-bodyparser 、 koa-favicon 。
喜欢 typescript + koa2 的童鞋可以一起来搞,方便自己也方便大家。