如何在打字稿中键入异步的潜在进程退出函数?
How to type an asynchronous potentially process-exiting function in typescript?
如何键入有时会退出进程的异步函数?
我希望能够像这样使用它:
function useResult(result:Result): void {
// ...
}
const result: Result | undefined = await getResult();
if (result === undefined) reportError({ fatal: true });
useResult(result) // I want the type of result to be `Result` here, not `Result | undefined`
const result: Result | undefined = await getResult();
if (result === undefined) reportError({ fatal: false });
useResult(result) // I want the type of result to be `Result | undefined` here
这里是有问题的函数:
import { exit } from "node:process";
interface ReportableError {
fatal?: true | false | undefined;
// ...
}
export async function reportError<E extends ReportableError>(
e: E,
) { // <-- How do I type the return of this function?
// Log the error, do some async stuff...
if (e.fatal) {
// Stop running
exit();
}
// Continue running
}
export async function reportError<E extends ReportableError>(
e: E,
): E["fatal"] extends true ? never : Promise<Result | undefined> {
// ...
}
我认为唯一可行的方法是,如果您将 reportError()
设为 overloaded function,并为“fatal”和“non-fatal”输入类型分别设置调用签名。
本质上你想要 reportError({fatal: true})
到 return the never
type because the function will never return. When you call a function whose call signature's return type is just never
, the compiler can use control flow analysis to narrow the types of variables based on reachability of code. This was implemented in microsoft/TypeScript#32695,并且只有在 return 类型被明确注释为 never
.[=45 时才会触发=]
既然你想要
if (result === undefined) reportError({ fatal: true });
将result
从Result | undefined
缩小到Result
,也就是说reportError()
必须return一个never
。所以 reportError
的一个调用签名应该看起来像
function reportError(e: { fatal: true }): never;
请注意,我们不能写 async function reportError(e: {fatal: true}): Promise<never>
,因为 Promise<never>
不会以我们想要的方式影响可达性...这在 microsoft/TypeScript#34955.
处被标记为错误
另一方面,当您调用 reportError()
时 fatal
不是 true
,您希望它只是一个异步函数 returns void
...好吧,Promise<void>
。像这样:
async function reportError(e: { fatal?: false | undefined }): Promise<void>;
而且我们必须将致命案例和 non-fatal 案例分离到它们自己的调用签名中,以便可达性分析按我们想要的方式工作。与 <E>(e: ReportableError) => E extends {fatal: true} ? never : Promise<void>
类似的单个 generic call signature that returns a conditional type 在概念上是相同的,但编译器不会将其视为断言。
好的,我们有两个调用签名,我们需要一个实现。完整的功能看起来像
function reportError(e: { fatal: true }): never;
async function reportError(e: { fatal?: false | undefined }): Promise<void>;
async function reportError(e: ReportableError) {
if (e.fatal) {
throw new Error("EXITING PROCESS OR SOMETHING");
}
}
让我们看看它是否按照我们想要的方式工作:
const result: Result | undefined = await getResult();
if (result === undefined) reportError({ fatal: true });
useResult(result) // okay
太好了,result
缩小了。如果我们改变死亡率水平:
const result: Result | undefined = await getResult();
if (result === undefined) reportError({ fatal: false });
useResult(result) // error! undefined is not assignable to Result
现在result
没有变窄,编译器不喜欢useResult(result)
因为useResult()
不接受undefined
.
如何键入有时会退出进程的异步函数?
我希望能够像这样使用它:
function useResult(result:Result): void {
// ...
}
const result: Result | undefined = await getResult();
if (result === undefined) reportError({ fatal: true });
useResult(result) // I want the type of result to be `Result` here, not `Result | undefined`
const result: Result | undefined = await getResult();
if (result === undefined) reportError({ fatal: false });
useResult(result) // I want the type of result to be `Result | undefined` here
这里是有问题的函数:
import { exit } from "node:process";
interface ReportableError {
fatal?: true | false | undefined;
// ...
}
export async function reportError<E extends ReportableError>(
e: E,
) { // <-- How do I type the return of this function?
// Log the error, do some async stuff...
if (e.fatal) {
// Stop running
exit();
}
// Continue running
}
export async function reportError<E extends ReportableError>(
e: E,
): E["fatal"] extends true ? never : Promise<Result | undefined> {
// ...
}
我认为唯一可行的方法是,如果您将 reportError()
设为 overloaded function,并为“fatal”和“non-fatal”输入类型分别设置调用签名。
本质上你想要 reportError({fatal: true})
到 return the never
type because the function will never return. When you call a function whose call signature's return type is just never
, the compiler can use control flow analysis to narrow the types of variables based on reachability of code. This was implemented in microsoft/TypeScript#32695,并且只有在 return 类型被明确注释为 never
.[=45 时才会触发=]
既然你想要
if (result === undefined) reportError({ fatal: true });
将result
从Result | undefined
缩小到Result
,也就是说reportError()
必须return一个never
。所以 reportError
的一个调用签名应该看起来像
function reportError(e: { fatal: true }): never;
请注意,我们不能写 async function reportError(e: {fatal: true}): Promise<never>
,因为 Promise<never>
不会以我们想要的方式影响可达性...这在 microsoft/TypeScript#34955.
另一方面,当您调用 reportError()
时 fatal
不是 true
,您希望它只是一个异步函数 returns void
...好吧,Promise<void>
。像这样:
async function reportError(e: { fatal?: false | undefined }): Promise<void>;
而且我们必须将致命案例和 non-fatal 案例分离到它们自己的调用签名中,以便可达性分析按我们想要的方式工作。与 <E>(e: ReportableError) => E extends {fatal: true} ? never : Promise<void>
类似的单个 generic call signature that returns a conditional type 在概念上是相同的,但编译器不会将其视为断言。
好的,我们有两个调用签名,我们需要一个实现。完整的功能看起来像
function reportError(e: { fatal: true }): never;
async function reportError(e: { fatal?: false | undefined }): Promise<void>;
async function reportError(e: ReportableError) {
if (e.fatal) {
throw new Error("EXITING PROCESS OR SOMETHING");
}
}
让我们看看它是否按照我们想要的方式工作:
const result: Result | undefined = await getResult();
if (result === undefined) reportError({ fatal: true });
useResult(result) // okay
太好了,result
缩小了。如果我们改变死亡率水平:
const result: Result | undefined = await getResult();
if (result === undefined) reportError({ fatal: false });
useResult(result) // error! undefined is not assignable to Result
现在result
没有变窄,编译器不喜欢useResult(result)
因为useResult()
不接受undefined
.