Issue
I have written a util function that is used to parse API exceptions and return modeled TS types.
interface NotFoundException {
type: 'notFound'
}
interface PermissionException {
type: 'permission'
}
interface BadRequestException {
type: 'badRequest'
}
// Example shape of API Error
interface APIError {
statusCode: number;
}
type KnownExceptions = NotFoundException | PermissionException | BadRequestException;
function exceptionHandler(err: unknown): KnownExceptions {
const statusCode = (err as APIError).statusCode ?? -1;
if (statusCode === 404) {
return {
type: 'notFound'
}
}
if (statusCode === 403) {
return {
type: 'permission'
}
}
if (statusCode === 400) {
return {
type: 'badRequest'
}
}
throw new Error('Unknown error', { cause: err });
}
Some APIs do not return all types, so I need to handle other types explicitly
interface APIInput {
prop: string;
}
interface APIOutput {
id: string;
}
function callAPI(input: APIInput): APIOutput | PermissionException | NotFoundException {
try {
//Mock API call
return {
id: 'id'
};
} catch (err: unknown) {
const e = exceptionHandler(err);
// Problem: This API (and others) cannot throw this/some exceptions
// Need a function that will restrict the types returned by exceptionHandler
// and throw 'Unexpected exception' for impossible cases
if (e.type === 'badRequest') {
throw new Error('Unexpected exception', { cause: err });
}
return e;
}
}
Is there a way I can write a function that restricts the return types of the union of exceptions to what I want and throw an Unexpected exception error for all other types ?
Since TS cannot take types as arguments I'm finding it hard to figure this out.
Solution
Something like this?
type ApiType = 'foo' | 'bar';
type AllowedExceptionsByApiType = {
foo: PermissionException | NotFoundException,
bar: BadRequestException
}
function exceptionHandler<T extends ApiType>(apiType: T, error: unknown): AllowedExceptionsByApiType[T] {
// impl
}
We declare possible api types in ApiType, then possible exception by each api type in AllowedExceptionsByApiType.
Then, using generics, the return type of exceptionHandler is constrained to those exception allowed by the api type.
Example:
function callApi(input: SomeInput): Output | PermissionException | NotFoundException {
try {
//API Call
} catch (err: unknown) {
const e = exceptionHandler('foo', err);
// error
if (e.type === 'badRequestException') {
throw new Error('Unexpected exception', err);
}
// ok
if (e.type === 'notFound') {
// impl
}
}
}
Answered By - Murolem
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.