Безопасная обработка ошибок без сторонних библиотек - PullRequest
0 голосов
/ 24 октября 2018

Я понимаю, что причина небезопасной обработки ошибок в ts не в том, что мы пропускаем предложение throws из функций.Поэтому из-за этого всякий раз, когда я делаю это:

function mighThrow(input: number): void {
  if (input === 1) {
    throw new TypeError('cannot be one')
  } else if(input === 2) {
    throw new SyntaxError('invalid syntax: 2')
  }
  console.log('all good', input)
}

Я не могу уловить ошибки с точным набором:

try {
  mighThrow(1)
} catch(e) {
  // e is any, even though it could be TypeError | SyntaxError
}

Проблема та же с Promise s,Аргумент функции catcher жестко запрограммирован any:

interface Promise<T> {
  then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
  catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}

Аргумент catch любой.

Итак мой вопрос как мне обойти это?Если я автор библиотеки, есть ли безопасный способ помочь пользователям понять, что именно они ловят?Мне известны такие решения, как добавление типа Either и вызов его в день, но это навязывает обработку ошибки на месте, которая просто превращает код во что-то похожее на то, как если бы язык проверял исключения.

1 Ответ

0 голосов
/ 24 октября 2018

Поскольку в TypeScript нет throws, то любое предложение будет выглядеть как обходной путь.Вы ищете решение, которое не приводит к дополнительным расходам для пользователя, который хочет игнорировать ошибки, но которое позволяет пользователям, которые хотят перехватывать ошибки, получать более строгие типы ошибок.

Как насчет чего-то подобного (для работы требуется TS3.1 и выше):

function mighThrow(input: number): void {
  if (input === 1) {
    throw new TypeError('cannot be one')
  } else if (input === 2) {
    throw new SyntaxError('invalid syntax: 2')
  }
  console.log('all good', input);
}
mighThrow.throws = undefined! as TypeError | SyntaxError;

Здесь мы использовали объявление свойства функции для объявления фантомасвойство под названием throws.Фантомное свойство - это свойство, которое на самом деле не существует во время выполнения, но компилятор считает, что оно существует, поэтому компилятор поддерживает дополнительную информацию о типе.В этом случае undefined! as TypeError | SyntaxError это просто undefined во время выполнения, но компилятор считает, что mighThrow имеет свойство throws типа TypeError | SyntaxError.

Теперь, когда люди хотят отлавливать ошибки, они могут использовать вспомогательную функцию:

type ThrowsType<T> = T extends { throws: infer E } ? E : unknown;
const asTypedError = <FS extends Function[]>(e: any, ...f: FS): ThrowsType<FS[number]> => e;

, например:

try {
  const x = mighThrow(123);
} catch (err) {
  const typedErr = asTypedError(mighThrow, err);
  // typedErr is now TypeError | SyntaxError
  if (typedErr instanceof TypeError) {
    typedErr; // TypeError
  } else {
    typedErr; // SyntaxError
  }       
}

Это не идеально для любогосредства.Требуется средство отслеживания ошибок, чтобы отслеживать, какие функции были вызваны сами.Блок try-catch, который окружает несколько функций, должен был бы передать все эти функции в asTypedError():

declare const alsoMightThrow: { throws: URIError } & ((input: string) => number);
try {
  const x = mighThrow(alsoMightThrow("hey"));
} catch (err) {     
  const typedErr = asTypedError(err, mighThrow, alsoMightThrow); // ?
  // typedErr: TypeError | SyntaxError | URIError
}

И на самом деле ничто не гарантирует , что перехваченная ошибка имеет тип (s) объявлено в свойстве throws ... это зависит от сопровождающего библиотеки, чтобы получить это право (что трудно, потому что многие вещи могут выдать TypeError во время выполнения).И наоборот, большинство функций не будет иметь такого свойства throws, поэтому у среднего разработчика нет особого стимула использовать этот тип перехвата ошибок.Если кто-то поместит функцию, отличную от throws, asTypedError выдаст ошибку unknown, которая вряд ли более полезна, чем any.Вы должны были бы научить пользователей библиотеки, как использовать эту вещь, и в этот момент может быть проще просто document проверить вашу функцию, а ловцы ошибок могут сделать свои собственные instanceof проверки для сужения отany.Это обходной путь для отсутствующей языковой функции, и это выглядит так.10


Хорошо, надеюсь, это поможет.Удачи!

...