node.js - Typescript - 为“promisifed”方法添加类型
问题描述
我有一个非常简单的模块,它包装了“fs”模块。该模块简单地“承诺”所有“fs”方法并导出新对象:
fsWrapper.ts
import Promise from "bluebird";
import * as fs from "fs";
const fsWrapper = Promise.promisifyAll(fs);
export = fsWrapper;
现在我可以使用这个包装器,而不是在每个调用者模块中“promisifiying”“fs”模块,如下所示:
main.ts
import fsWrapper from "./fsWrapper";
function test(): void {
fsWrapper.readFileAsync("tst.txt", "utf8")
.then((data: Buffer) => {
console.log("data:", data.toString());
})
}
这当然不适用于打字稿,因为“fs”不包含该readFileAsync
方法并且我收到编译器错误。
在搜索正确键入此包装器的方法时,我发现了以下打字稿问题。
使用它的方法,我可以创建自己的fsWrapper.d.ts
,我需要在其中手动添加 *Async 方法,类似于以下内容:
fsWrapper.d.ts
import Promise from "bluebird";
declare module "fs" {
export function readFileAsync(path: string, options: { encoding?: string | null; flag?: string; } | string | undefined | null) : Promise<Buffer>;
...
}
这里的问题是:
- 手动添加所有需要的方法既乏味又容易出错。
- 如果这些方法在未来的版本中会改变,我不知道我的代码会继续编译,我会遇到运行时异常。
我知道这util.promisify
是正确的类型,因此想到了用这些新方法以某种方式扩展“fs”,类似于以下内容:
fsWrapperTest.ts
import * as fs from "fs";
import { promisify } from "util";
let fsWrapper = fs;
fsWrapper.readFileAsync = promisify(fs.readFile);
export = fsWrapper;
但这会输出一个错误,表明我无法扩展现有模块:
error TS2551: Property 'readFileAsync' does not exist on type 'typeof "fs"'. Did you mean 'readFileSync'?
有什么方法可以让我在保持“最新”输入和使用 Intellisense 的同时正确输入这个包装器?
编辑:
澄清一下,我了解如何创建一个对象,该对象将承诺所有“fs”方法(fsWrapper.ts
上面的原始方法就是这种情况)。
我正在努力为 Intellisense 使用正确键入它。例如,运行以下命令:
import * as fs from "fs";
import { promisify } from "util";
let fsWrapper = Object.keys(fs).reduce((p: typeof fs, v: string) => { p[v] = promisify(fs[v]); return p }, {})
会给我一个fsWrapper: {}
对象的类型。
我想将所有“fs”方法+新的“promisifed”方法作为它的类型。
编辑 - 选择的解决方案
我最终通过declare module 'fs'
使用我在代码中使用的所有 *Async 方法扩展“fs”模块来使用该方法。不理想,但似乎没有更好的选择..
解决方案
如果您使用的是 TypeScript 3.0 或更高版本,映射类型和参数元组的改进应该可以实现这一点。例如:
type UnpackedPromise<T> = T extends Promise<infer U> ? U : T
type GenericFunction<TS extends any[], R> = (...args: TS) => R
type Promisify<T> = {
[K in keyof T]: T[K] extends GenericFunction<infer TS, infer R>
? (...args: TS) => Promise<UnpackedPromise<R>>
: never
}
这会创建一个用于解包 Promise 的类型,一个用于泛型函数的类型,然后是一个用于 Promisified 版本的推断映射类型,该类型将每个键映射到一个新值,即前一个函数的 Promisified 版本。要使用该类型(游乐场链接):
const original = {
one: () => 1,
two: () => 2,
add: (a: number, b: number) => a + b
}
const promisified = original as unknown as Promisify<typeof original>
promisified.one()
promisified.add(1, 2)
对于您的示例,以下是使用方法:
import * as fs from "fs";
import { promisify } from "util";
let fsWrapper = Object
.keys(fs)
.reduce((p: typeof fs, v: string) => { p[v] = promisify(fs[v]); return p }, {}) as unknown as Promisify<typeof fs>
请注意,您先转换为 unknown 然后再转换为Promisify<*>
- 这是因为默认情况下它会认为映射类型可能是错误的。
推荐阅读
- r - 删除空格并将字符转换为整个数据帧的较低值
- android - 来自 Firebase storageReference 的 getBytes 不起作用
- pandas - 熊猫 groupby 和排名 - 重复的排名相同
- excel - 在 IE 窗口中填写表格:按下按钮并填写文本框
- mongodb - $lookup 在 MongoDB 中工作,但不与 mongoose 一起工作
- layout - 如何在 amp-story 的上三分之一处居中文本?
- javascript - CredentialsError:配置节点中缺少凭据
- ruby-on-rails - 表单中的第一个参数不能包含 nil 或为空,但它不应为 nil 或空
- prism - Prism 7.1.0 使用 app.config 加载模块
- python - 在 django 项目中组织模板