Home > Back-end >  how to avoid the compiler error for enumerable: false properties
how to avoid the compiler error for enumerable: false properties

Time:09-15

I'm trying to learn TS and I came to a weird case scenario.

I have an object with a property set as non-enumerable:

let person = {
  name: 'Harry'
}

Object.defineProperty(person,'salary',{enumerable: false, value : 15});

console.log(person.salary); // 15

This works at runtime, however, the compiler is going to error:

Property 'salary' does not exist on type '{ name: string; }'

which is expected.

I could bypass this setting the type of person as any, but it doesn't feel like a clean solution.

Is there another way to do it? The non-null assertion operator doesn't work for this.

I'd appreciate if anyone can share some knowledge about this.

CodePudding user response:

While you could change the type annotation for person, maybe like:

let person: { name: string; salary?: number } = {
  name: 'Harry'
};

or even:

let person = {
  name: 'Harry'
} as { name: string; salary: number };

The safest method is probably (ab)using assertion functions in version 3.7:

function defineProperty<
    O,
    Property extends PropertyKey,
    Value
>(o: O, key: Property, attributes: PropertyDescriptor & ThisType<any> & { value: Value }): asserts o is O & Record<Property, Value> {
    Object.defineProperty(o, key, attributes);
}

It's essentially a wrapper function for Object.defineProperty that tells TypeScript, if this function doesn't throw an error, then the type of the given value o is O & Record<Property, Value>.

So when you use it, you get a type like this:

defineProperty(person,'salary',{enumerable: false, value : 15});

person
// ^? { name: string; } & Record<"salary", number>

Playground


And if you really want to, you can also make the resulting type cleaner by changing the assertion to this:

asserts o is (O & Record<Property, Value> extends infer T extends O ? { [K in keyof T]: T[K] } : O)

As you can see here when hovering over person, the type is now { name: string; salary: number; } instead of an ugly intersection.

  • Related