前阵子 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
的童鞋可以一起来搞,方便自己也方便大家。