Nekron SN
Nekron SN

Reputation: 101

Multi level type guards

Given the following code, how can I modify the isAppErrorOfKind type guard in order for the return type to be AppErrorHttp or AppErrorNetwork based on the kind specified?

type AppError = AppErrorHttp | AppErrorNetwork | AppErrorUnsupported;

interface AppErrorHttp {
  kind: 'http';
  status: number;
}

interface AppErrorNetwork {
  kind: 'network';
  reason: string;
}

interface AppErrorUnsupported {
  kind: 'unsupported';
  api: string;
}

function isAppError(error: Error | AppError): error is AppError {
  if (error === undefined || error === null) return false;
  if (typeof(error) !== 'object') return false;
  return 'kind' in error;
}

function isAppErrorOfKind(error: Error | AppError, kind: AppError['kind']): error is XXX {
  if (!isAppError(error)) return false;
  return error.kind === kind;
}

Upvotes: 2

Views: 47

Answers (2)

You need to infer kind argument:

type AppError = AppErrorHttp | AppErrorNetwork | AppErrorUnsupported;

interface AppErrorHttp {
  kind: 'http';
  status: number;
}

interface AppErrorNetwork {
  kind: 'network';
  reason: string;
}

interface AppErrorUnsupported {
  kind: 'unsupported';
  api: string;
}


function isAppError(error: Error | AppError): error is AppError {
  if (error === undefined || error === null) return false;
  if (typeof error !== 'object') return false;
  return 'kind' in error;
}

function isAppErrorOfKind<Kind extends AppError['kind']>(
  error: Error | AppError,
  kind: Kind
): error is Extract<AppError, { kind: Kind }> {
  if (!isAppError(error)) return false;
  return error.kind === kind;
}

const foo = (arg: AppError) => {
  if (isAppErrorOfKind(arg, 'network')) {
    arg // AppErrorHttp
  } else {
    arg // AppErrorHttp | AppErrorUnsupported
  }
}

Playground

Upvotes: 2

Ted
Ted

Reputation: 755

If I've understood your question correctly, I would perhaps use a generic type constraint:

function isAppErrorOfKind<T extends AppError>(error: T, kind: AppError["kind"]): error is T {
  if (!isAppError(error)) return false;
  return error.kind === kind;
}

You can then only pass in an error of type AppError, extending AppErrorHttp and AppErrorNetwork.

Upvotes: 1

Related Questions