如何能够基于联合类型区分两个对象?

问题描述 投票:1回答:1

我想实现tryInline,它应该尝试调用一个函数并根据调用的成功或失败返回两种对象。

如果成功调用了fn参数,则tryInline应该返回:

{
  ok: true,
  data: <The value returned by `fn`>,
  error: null
}

如果fn引发错误,则tryInline应该返回:

{
  ok: false,
  data: null,
  error: Error
}

我设法执行以下操作:

type Result<D> =
  | { ok: true; data: D; error: null }
  | { ok: false; data: null; error: Error };

function tryInline<Fn extends (...args: any[]) => any>(
  fn: Fn,
  ...args: Parameters<Fn>
): Result<ReturnType<Fn>> {
  try {
    const data = fn(...args);

    return { ok: true, data, error: null };
  } catch (error) {
    return { ok: false, data: null, error: error };
  }
}

const { ok, data, error } = tryInline((a: number, b: number) => a + b, 1, 3);

if (ok) {
  console.log(ok); // inferred type: `true` 👍
  console.log(data); // inferred type: `number | null` 👎 Should be `number`
  console.log(error); // inferred type: `Error | null` 👎 Should be `null`
}

if (!ok) {
  console.log(ok); // inferred type: `boolean` 👎 Should be `false`
  console.log(data); // inferred type: `number | null` 👎 Should be `null`
  console.log(error); // inferred type: `Error | null` 👎 Should be `Error`
}

Playground link.

但是,我希望能够正确推断if块中的类型,这不是当前情况。

是否有解决此推断问题的方法?

typescript type-inference
1个回答
4
投票

您需要使用解构after条件分支,因为TS链接的类型是对象,而不是普通变量。分解它们时,okdata之间的链接会丢失。此外,我认为这样比较干净,因为您正在破坏真正需要的东西。


const result = tryInline((a: number, b: number) => a + b, 1, 3);

if (result.ok) {
  const { data } = result;
  console.log(data); // inferred type: `number` 
}

if (!result.ok) {
  const { error } = result;
  console.log(error); // inferred type: `Error`
}

如果您只是想在不分支的情况下进行分解,那么对我来说,只执行const { data, error } = tryInline(...)并继续进行if (data) { ... }似乎更合乎逻辑。

Playground Link

© www.soinside.com 2019 - 2024. All rights reserved.