Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript check if property in object in typesafe way

The code

const obj = {};
if ('a' in obj) console.log(42);

Is not typescript (no error). I see why that could be. Additionally, in TS 2.8.1 "in" serves as type guard.

But nevertheless, is there an way to check if property exists, but error out if the property is not defined in the interface of obj?

interface Obj{
   a: any;
}

I'm not talking about checking for undefined...

like image 416
cdbeelala89 Avatar asked Apr 07 '18 12:04

cdbeelala89


People also ask

How do you check if a property exists in an object TypeScript?

To check if a property exists in an object in TypeScript: Mark the specific property as optional in the object's type. Use a type guard to check if the property exists in the object. If accessing the property in the object does not return a value of undefined , it exists in the object.

How do I use hasOwnProperty in TypeScript?

The hasOwnProperty() method returns true if the specified property is a direct property of the object — even if the value is null or undefined . The method returns false if the property is inherited, or has not been declared at all.

What is type checking in TypeScript?

TypeScript type check is used to validate the type of any variable at runtime. Type checking has proven to be a good feature for most JavaScript developers. We will start with the most basic library provided by TypeScript, which will directly validate the type of single variables in the code.

Does not exist on type TypeScript?

The "Property does not exist on type '{}'" error occurs when we try to access or set a property that is not contained in the object's type. To solve the error, type the object properties explicitly or use a type with variable key names.


3 Answers

You don't get an error because you use a string to check if the property exists.

You will get the error this way:

interface Obj{
   a: any;
}

const obj: Obj = { a: "test" };

if (obj.b)          // this is not allowed
if ("b" in obj)     // no error because you use string

If you want type checking to work for string properties you could add index signatures using this example

like image 69
Kokodoko Avatar answered Oct 21 '22 09:10

Kokodoko


The following handle function checks hypothetical server response typesafe-way:

/**
 * A type guard. Checks if given object x has the key.
 */
const has = <K extends string>(
  key: K,
  x: object,
): x is { [key in K]: unknown } => (
  key in x
);

function handle(response: unknown) {
  if (
    typeof response !== 'object'
    || response == null
    || !has('items', response)
    || !has('meta', response)
  ) {
    // TODO: Paste a proper error handling here.
    throw new Error('Invalid response!');
  }

  console.log(response.items);
  console.log(response.meta);
}

Playground Link. Function has should probably be kept in a separate utilities module.

like image 38
quasiyoke Avatar answered Oct 21 '22 11:10

quasiyoke


You can implement your own wrapper function around hasOwnProperty that does type narrowing.

function hasOwnProperty<T, K extends PropertyKey>(
    obj: T,
    prop: K
): obj is T & Record<K, unknown> {
    return Object.prototype.hasOwnProperty.call(obj, prop);
}

I found this solution here: TypeScript type narrowing not working when looping

Usage:

const obj = {
    a: "what",
    b: "ever"
} as { a: string }

obj.b // Type error: Property 'b' does not exist on type '{ a: string; }'

if (hasOwnProperty(obj, "b")) {
    // obj is no longer { a: string } but is now
    // of type { a: string } & Record<"b", unknown>
    console.log(obj.b)
}

The limitation with this approach is that you only get a Record back with the single key added that you specified. This might be fine for some needs, but if you need a more general solution then I suggest a library like Zod which can validate a complex object and give you the full type: https://github.com/colinhacks/zod

like image 42
Sámal Rasmussen Avatar answered Oct 21 '22 11:10

Sámal Rasmussen