Home > Mobile >  TS: How to check if unknown object has a property?
TS: How to check if unknown object has a property?

Time:11-26

I have to write a function to recognize if unknown value has specific property. Unfortunately typescript still don't know what kind of value I have.

Here is my code:

export function isMySpecificError(error: unknown) {
  if (error && typeof error === 'object' && 'code' in error && error.hasOwnProperty('code')) {

    //TS2339: Property 'code' does not exist on type 'object'.
    return typeof error.code === 'string';
  }
}

I know my if statement could be written differently but I can't understand why typescript still doesn't know if property 'code' exist in 'error' after double check 'code' in error && error.hasOwnProperty('code')

Here is my current solution which works, but I had to declare new type for 'error' instead of check it:

export function isMySpecificError(error: unknown): error is MySpecificError {
  if (error && typeof error === 'object' && error.hasOwnProperty('code')) {
    const errorWithCodeProperty = error as { code: unknown };
    return typeof errorWithCodeProperty.code === 'string' && errorWithCodeProperty.code.startsWith('someErrorCodeValue');
  }
  return false;
}

Could it be simplified? Is it possible to check differently if unknown object has a specific property?

CodePudding user response:

Your current solution is a good workaround for the moment and you might want to keep it as it is. But looking at the future, TypeScript 4.9 will introduce improvements to control flow analysis regarding the in operator.

Up to version 4.9, using the in operator on values of type object had no impact on the narrowing of the type. But in future versions, types will be intersected with Record<"propName", unknown> which makes the error disappear.

// version 4.9

function isMySpecificError(error: unknown) {
  if (error && typeof error === 'object' && 'code' in error && error.hasOwnProperty('code')) {

    error
//  ^? error: object & Record<"code", unknown>

    return typeof error.code === 'string';
  }
  return false;
}

Playground 4.9

CodePudding user response:

interface ErrorWithCode<T extends number = number> extends Error {
  code: T
}

function isErrorWithCode<T extends number>(
  err: ErrorWithCode<T> | unknown
): err is ErrorWithCode<T> {
  return err instanceof Error && 'code' in err;
}

let e = Object.assign(new Error(), {code: 444 as const})

if (isErrorWithCode(e)) {
  e.code // 444
  // ^?
}

let e1 = new Error()
if (isErrorWithCode(e1)) {
  e1.code // number
  // ^?
}

Playground

  • Related