Как уметь различать два объекта на основе типа объединения? - PullRequest
1 голос
/ 25 апреля 2020

Я хочу реализовать 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`
}

ссылка на игровую площадку.

Однако я хочу иметь возможность правильно выводить типы внутри блоков if, что не соответствует текущему случаю.

Is Есть ли способ решить эту проблему вывода?

1 Ответ

4 голосов
/ 25 апреля 2020

Вам необходимо использовать деструктурирование после ветвления условия, поскольку TS связывает тип с объектом, а не с простыми переменными. Когда вы их уничтожаете, связь между ok и data теряется. Кроме того, я думаю, что это чище, потому что вы разрушаете то, что вам действительно нужно.


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

...