我有一个非常简单的模块,其中包装了“ fs”模块。 此模块仅“允许”所有“ fs”方法并导出新对象:
fsWrapper.ts
import Promise from "bluebird";
import * as fs from "fs";
const fsWrapper = Promise.promisifyAll(fs);
export = fsWrapper;
现在,我可以使用此包装器来代替在每个调用者模块中“承诺”使用“ 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
方法,并且我收到编译器错误。
在搜索正确键入此包装器的方式时,我发现了following typescript issue。
使用它的方法,我可以创建自己的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'模块。
不是很理想,但似乎没有更好的选择。
答案 0 :(得分:1)
在github上有一个专门用于此目的的线程,请参见https://github.com/Microsoft/TypeScript/issues/8685
因此,您可以执行以下操作:
import { Promise } from 'bluebird';
import * as fs from "fs";
declare module "fs" {
interface fs {
[method: string]: any;
}
}
const fsWrapper = Promise.promisifyAll(fs);
export = fsWrapper;
这与您为解决方法调用问题所做的工作非常相似。
请注意,据我所知,唯一允许完整Intellisense的方法是按照打字稿文档https://www.typescriptlang.org/docs/handbook/declaration-merging.html
中所述进行模块扩充但是,这意味着您需要手动为上面的模块结构内部的promisified生成的新方法编写接口。即
declare module "fs" {
interface fs {
readFileAsync(... ),
writeFileAsyng(... ),
etc ...
}
}
答案 1 :(得分:1)
如果您使用的是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的类型,一个泛型函数的类型,然后为有承诺的版本创建一个推断的映射类型,该映射类型将每个键映射到一个新值,该值是前一个函数的有承诺的版本。要使用该类型(playground link):
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>
请注意,您先转换为未知,然后转换为Promisify<*>
-这是因为默认情况下,它将认为映射类型可能是错误的。
答案 2 :(得分:1)
Node.js> = v10.x开始提供已经承诺的功能。
您可以通过require("fs").promises
或require("fs/promises")
(> = v14.x)使用它们
https://nodejs.org/docs/latest-v10.x/api/fs.html#fs_fs_promises_api https://nodejs.org/docs/latest-v14.x/api/fs.html#fs_fs_promises_api
我用
const fs = { ...require("fs"), ...require("fs/promises") };