前阵子koa2
发布了,一些中间件也增加了对koa2
的支持,这当然是大大的好事了。但是,像我这样喜欢用typescript
+koa2
写node
的人来说,某个中间件没对应的.d.ts
文件会是件很蛋疼的事。
没人写只能自己来了,写过之后会发现其实很简单,还能对那些中间件有更深入的了解。下面介绍下怎么找到支持koa2
的中间件以及怎么写对应的.d.ts
文件:
middlewares
koa
的中间件可以在koa
的wiki上看到,里面列出了哪些中间件支持koa2
,我们直接在上面找就行了。
为了方便,我写了个命令行工具koa2-middlewares来查看这些中间件(当然是用typescript
写的啦)。全局安装后,通过命令行koams list
可以查看所有的koa2
中间件,
koams list -i
会过滤掉已经有.d.ts
文件的中间件。具体用法参见该工具的README。
definitions
知道有哪些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
的童鞋可以一起来搞,方便自己也方便大家。