可以承诺的类型函数
Type functions that can be promsified
我正在努力向 node-vagrant NPM library to use in Typescript, to be contributed under DefinitelyTyped. However, one of the methods of the library is promisify 添加单独的类型,这使得库中的所有其他函数然后 return 成为承诺而不是回调。
鉴于我只能控制添加类型 (.d.ts
) 文件,有没有办法向 tsc
表明调用自定义 promisify 函数的结果在函数上,还是其他一些动力机制?或者只是我为函数的两种用法提供类型,而用户只需要确保他们选择正确?
一个最小的例子是 JS 文件是:
module.exports.foo = function (cb) {
cb(null, 'foo');
};
module.exports.promisify = function () {
module.exports.foo = util.promisify(module.exports.foo);
}
我输入的(在 .d.ts
文件中)是:
export function foo(cb: (err: null | string, out: string) => void): void;
export function promisify(): void;
现在,当我使用打字时:
import foo = require('foo');
foo.foo((err, out) => { console.log(err, out); });
foo.promisify();
foo.foo().then(out => console.log(out)).catch(err => console.log(err));
最后一行引发了 TSC 错误。解决方案是仅在函数签名上同时声明回调和承诺,并让最终用户适当地决定使用哪一个,或者 TypeScript 中是否有某种机制来动态切换函数的 return 信息?
从上面看,最后的判决是干嘛的:
export function foo(): Promise<string>;
export function foo(cb: (err: null | string, out: string) => void): void;
并且如上所述,只是让最终用户弄清楚他们是想要回调还是承诺?
@types/node 对 util.promisify
使用以下定义:
interface CustomPromisify<TCustom extends Function> extends Function {
__promisify__: TCustom;
}
function callbackify(fn: () => Promise<void>): (callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<TResult>(fn: () => Promise<TResult>): (callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void;
function callbackify<T1>(fn: (arg1: T1) => Promise<void>): (arg1: T1, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, TResult>(fn: (arg1: T1) => Promise<TResult>): (arg1: T1, callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void;
function callbackify<T1, T2>(fn: (arg1: T1, arg2: T2) => Promise<void>): (arg1: T1, arg2: T2, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, TResult>(fn: (arg1: T1, arg2: T2) => Promise<TResult>): (arg1: T1, arg2: T2, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function callbackify<T1, T2, T3>(fn: (arg1: T1, arg2: T2, arg3: T3) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, T3, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3) => Promise<TResult>): (arg1: T1, arg2: T2, arg3: T3, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function callbackify<T1, T2, T3, T4>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, T3, T4, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<TResult>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function callbackify<T1, T2, T3, T4, T5>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, T3, T4, T5, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<TResult>,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function callbackify<T1, T2, T3, T4, T5, T6>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) => Promise<void>,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, T3, T4, T5, T6, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) => Promise<TResult>
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function promisify<TCustom extends Function>(fn: CustomPromisify<TCustom>): TCustom;
function promisify<TResult>(fn: (callback: (err: Error | null, result: TResult) => void) => void): () => Promise<TResult>;
function promisify(fn: (callback: (err?: Error | null) => void) => void): () => Promise<void>;
function promisify<T1, TResult>(fn: (arg1: T1, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1) => Promise<TResult>;
function promisify<T1>(fn: (arg1: T1, callback: (err?: Error | null) => void) => void): (arg1: T1) => Promise<void>;
function promisify<T1, T2, TResult>(fn: (arg1: T1, arg2: T2, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1, arg2: T2) => Promise<TResult>;
function promisify<T1, T2>(fn: (arg1: T1, arg2: T2, callback: (err?: Error | null) => void) => void): (arg1: T1, arg2: T2) => Promise<void>;
function promisify<T1, T2, T3, TResult>(fn: (arg1: T1, arg2: T2, arg3: T3, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1, arg2: T2, arg3: T3) => Promise<TResult>;
function promisify<T1, T2, T3>(fn: (arg1: T1, arg2: T2, arg3: T3, callback: (err?: Error | null) => void) => void): (arg1: T1, arg2: T2, arg3: T3) => Promise<void>;
function promisify<T1, T2, T3, T4, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: Error | null, result: TResult) => void) => void,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<TResult>;
function promisify<T1, T2, T3, T4>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err?: Error | null) => void) => void): (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<void>;
function promisify<T1, T2, T3, T4, T5, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: Error | null, result: TResult) => void) => void,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<TResult>;
function promisify<T1, T2, T3, T4, T5>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err?: Error | null) => void) => void,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<void>;
function promisify(fn: Function): Function;
这是处理默认 promisify 的非常完整的方法。你可能 可以把它精简一点。
Given that I only have control over adding the typing (.d.ts
) file, is there a way to indicate to tsc
that the result of calling that custom promisify function is on the functions, or some other mechanism of dynamism?
据我所知,这在 TypeScript 中是不可能的。 foo
的类型是静态的,虽然 TypeScript 会进行一些基于控制流的类型分析,但它无法使用函数调用来切换 foo
的类型。
如果 promisify()
返回一个包含 promisified 函数的新对象而不是就地切换它们,那么为此创建准确的类型定义会容易得多。但是由于您无法控制源代码,所以我看到的仅有两个选项是:
过载
这是您在问题中已经提到的解决方案。通过为每个方法声明两个签名,所有用法都将有效,但用户有责任确保他们调用 promisify
如果他们想使用承诺。
export function foo(): Promise<string>;
export function foo(cb: (err: null | string, out: string) => void): void;
联盟
或者,整个模块可以导出一个联合类型,要求消费者在使用 promisified API 之前转换它。它可能看起来像这样:
export interface CallbackFoo {
foo(cb: (err: null | string, out: string) => void): void;
promisify(): void;
}
export interface PromiseFoo {
foo(): Promise<string>;
promisify(): void;
}
declare const _: CallbackFoo | PromiseFoo;
export default _;
以及用法:
import foo, { PromiseFoo } from 'foo';
foo.foo((err, out) => { console.log(err, out); });
foo.promisify();
(foo as PromiseFoo).foo().then(out => console.log(out)).catch(err => console.log(err));
现在你可能不想在使用它的时候一直施放它。一个更简洁的解决方案可能是创建一个单独的文件,导入 foo
,调用 promisify
并导出它:
import foo, { PromiseFoo } from './foo';
foo.promisify();
export default foo as PromiseFoo;
然而,这当然必须在最终用户方面。
我正在努力向 node-vagrant NPM library to use in Typescript, to be contributed under DefinitelyTyped. However, one of the methods of the library is promisify 添加单独的类型,这使得库中的所有其他函数然后 return 成为承诺而不是回调。
鉴于我只能控制添加类型 (.d.ts
) 文件,有没有办法向 tsc
表明调用自定义 promisify 函数的结果在函数上,还是其他一些动力机制?或者只是我为函数的两种用法提供类型,而用户只需要确保他们选择正确?
一个最小的例子是 JS 文件是:
module.exports.foo = function (cb) {
cb(null, 'foo');
};
module.exports.promisify = function () {
module.exports.foo = util.promisify(module.exports.foo);
}
我输入的(在 .d.ts
文件中)是:
export function foo(cb: (err: null | string, out: string) => void): void;
export function promisify(): void;
现在,当我使用打字时:
import foo = require('foo');
foo.foo((err, out) => { console.log(err, out); });
foo.promisify();
foo.foo().then(out => console.log(out)).catch(err => console.log(err));
最后一行引发了 TSC 错误。解决方案是仅在函数签名上同时声明回调和承诺,并让最终用户适当地决定使用哪一个,或者 TypeScript 中是否有某种机制来动态切换函数的 return 信息?
从上面看,最后的判决是干嘛的:
export function foo(): Promise<string>;
export function foo(cb: (err: null | string, out: string) => void): void;
并且如上所述,只是让最终用户弄清楚他们是想要回调还是承诺?
@types/node 对 util.promisify
使用以下定义:
interface CustomPromisify<TCustom extends Function> extends Function {
__promisify__: TCustom;
}
function callbackify(fn: () => Promise<void>): (callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<TResult>(fn: () => Promise<TResult>): (callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void;
function callbackify<T1>(fn: (arg1: T1) => Promise<void>): (arg1: T1, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, TResult>(fn: (arg1: T1) => Promise<TResult>): (arg1: T1, callback: (err: NodeJS.ErrnoException, result: TResult) => void) => void;
function callbackify<T1, T2>(fn: (arg1: T1, arg2: T2) => Promise<void>): (arg1: T1, arg2: T2, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, TResult>(fn: (arg1: T1, arg2: T2) => Promise<TResult>): (arg1: T1, arg2: T2, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function callbackify<T1, T2, T3>(fn: (arg1: T1, arg2: T2, arg3: T3) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, T3, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3) => Promise<TResult>): (arg1: T1, arg2: T2, arg3: T3, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function callbackify<T1, T2, T3, T4>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, T3, T4, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<TResult>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function callbackify<T1, T2, T3, T4, T5>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<void>): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, T3, T4, T5, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<TResult>,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function callbackify<T1, T2, T3, T4, T5, T6>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) => Promise<void>,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, callback: (err: NodeJS.ErrnoException) => void) => void;
function callbackify<T1, T2, T3, T4, T5, T6, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) => Promise<TResult>
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, callback: (err: NodeJS.ErrnoException | null, result: TResult) => void) => void;
function promisify<TCustom extends Function>(fn: CustomPromisify<TCustom>): TCustom;
function promisify<TResult>(fn: (callback: (err: Error | null, result: TResult) => void) => void): () => Promise<TResult>;
function promisify(fn: (callback: (err?: Error | null) => void) => void): () => Promise<void>;
function promisify<T1, TResult>(fn: (arg1: T1, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1) => Promise<TResult>;
function promisify<T1>(fn: (arg1: T1, callback: (err?: Error | null) => void) => void): (arg1: T1) => Promise<void>;
function promisify<T1, T2, TResult>(fn: (arg1: T1, arg2: T2, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1, arg2: T2) => Promise<TResult>;
function promisify<T1, T2>(fn: (arg1: T1, arg2: T2, callback: (err?: Error | null) => void) => void): (arg1: T1, arg2: T2) => Promise<void>;
function promisify<T1, T2, T3, TResult>(fn: (arg1: T1, arg2: T2, arg3: T3, callback: (err: Error | null, result: TResult) => void) => void): (arg1: T1, arg2: T2, arg3: T3) => Promise<TResult>;
function promisify<T1, T2, T3>(fn: (arg1: T1, arg2: T2, arg3: T3, callback: (err?: Error | null) => void) => void): (arg1: T1, arg2: T2, arg3: T3) => Promise<void>;
function promisify<T1, T2, T3, T4, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err: Error | null, result: TResult) => void) => void,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<TResult>;
function promisify<T1, T2, T3, T4>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, callback: (err?: Error | null) => void) => void): (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<void>;
function promisify<T1, T2, T3, T4, T5, TResult>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err: Error | null, result: TResult) => void) => void,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<TResult>;
function promisify<T1, T2, T3, T4, T5>(
fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, callback: (err?: Error | null) => void) => void,
): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => Promise<void>;
function promisify(fn: Function): Function;
这是处理默认 promisify 的非常完整的方法。你可能 可以把它精简一点。
Given that I only have control over adding the typing (
.d.ts
) file, is there a way to indicate totsc
that the result of calling that custom promisify function is on the functions, or some other mechanism of dynamism?
据我所知,这在 TypeScript 中是不可能的。 foo
的类型是静态的,虽然 TypeScript 会进行一些基于控制流的类型分析,但它无法使用函数调用来切换 foo
的类型。
如果 promisify()
返回一个包含 promisified 函数的新对象而不是就地切换它们,那么为此创建准确的类型定义会容易得多。但是由于您无法控制源代码,所以我看到的仅有两个选项是:
过载
这是您在问题中已经提到的解决方案。通过为每个方法声明两个签名,所有用法都将有效,但用户有责任确保他们调用 promisify
如果他们想使用承诺。
export function foo(): Promise<string>;
export function foo(cb: (err: null | string, out: string) => void): void;
联盟
或者,整个模块可以导出一个联合类型,要求消费者在使用 promisified API 之前转换它。它可能看起来像这样:
export interface CallbackFoo {
foo(cb: (err: null | string, out: string) => void): void;
promisify(): void;
}
export interface PromiseFoo {
foo(): Promise<string>;
promisify(): void;
}
declare const _: CallbackFoo | PromiseFoo;
export default _;
以及用法:
import foo, { PromiseFoo } from 'foo';
foo.foo((err, out) => { console.log(err, out); });
foo.promisify();
(foo as PromiseFoo).foo().then(out => console.log(out)).catch(err => console.log(err));
现在你可能不想在使用它的时候一直施放它。一个更简洁的解决方案可能是创建一个单独的文件,导入 foo
,调用 promisify
并导出它:
import foo, { PromiseFoo } from './foo';
foo.promisify();
export default foo as PromiseFoo;
然而,这当然必须在最终用户方面。