Поскольку в 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
Хорошо, надеюсь, это поможет.Удачи!