From b86460065acbd1a511631e668205b41b1e47946c Mon Sep 17 00:00:00 2001 From: uhyo Date: Fri, 29 Apr 2022 23:07:38 +0900 Subject: [PATCH] feat: improve hasOwnProperty types fixes #4 --- build/replacement.ts | 2 + generated/lib.es2022.object.d.ts | 38 ++++++++-- generated/lib.es5.d.ts | 125 ++++++++++++++++++++++--------- lib/lib.es2022.object.d.ts | 19 +++++ lib/lib.es5.d.ts | 47 ++++++++++++ tests/src/es2022.object.ts | 33 ++++++++ tests/src/es5.ts | 85 ++++++++++++++------- 7 files changed, 279 insertions(+), 70 deletions(-) create mode 100644 lib/lib.es2022.object.d.ts create mode 100644 tests/src/es2022.object.ts diff --git a/build/replacement.ts b/build/replacement.ts index 976d920..8953acf 100644 --- a/build/replacement.ts +++ b/build/replacement.ts @@ -11,6 +11,7 @@ export const replacement = new Map([ "ReadonlyArray", "Array", "PromiseConstructorLike", + "Object", ]), ], [ @@ -50,4 +51,5 @@ export const replacement = new Map([ ["es2019.object.d.ts", new Set(["ObjectConstructor"])], ["es2021.promise.d.ts", new Set(["AggregateError"])], ["es2021.string.d.ts", new Set(["String"])], + ["es2022.object.d.ts", new Set(["ObjectConstructor"])], ]); diff --git a/generated/lib.es2022.object.d.ts b/generated/lib.es2022.object.d.ts index 7325ef3..ac00a6d 100644 --- a/generated/lib.es2022.object.d.ts +++ b/generated/lib.es2022.object.d.ts @@ -1,8 +1,30 @@ -interface ObjectConstructor { - /** - * Determines whether an object has a property with the specified name. - * @param o An object. - * @param v A property name. - */ - hasOwn(o: object, v: PropertyKey): boolean; -} +interface ObjectConstructor { + /** + * Determines whether an object has a property with the specified name. + * @param o An object. + * @param v A property name. + */ + hasOwn( + o: object, + v: Key + ): o is string extends Key + ? {} + : number extends Key + ? {} + : symbol extends Key + ? {} + : Key extends PropertyKey + ? { [key in Key]: unknown } + : {}; +} +// -------------------- + +// interface ObjectConstructor { +// /** +// * Determines whether an object has a property with the specified name. +// * @param o An object. +// * @param v A property name. +// */ +// hasOwn(o: object, v: PropertyKey): boolean; +// } + diff --git a/generated/lib.es5.d.ts b/generated/lib.es5.d.ts index 5e8087a..2e4b959 100644 --- a/generated/lib.es5.d.ts +++ b/generated/lib.es5.d.ts @@ -15,6 +15,53 @@ type UnionToIntersection = ( */ declare function eval(x: string): unknown; +interface Object { + /** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */ + constructor: Function; + + /** Returns a string representation of an object. */ + toString(): string; + + /** Returns a date converted to a string using the current locale. */ + toLocaleString(): string; + + /** Returns the primitive value of the specified object. */ + valueOf(): Object; + + /** + * Determines whether an object has a property with the specified name. + * @param v A property name. + */ + hasOwnProperty( + v: Key + ): this is string extends Key + ? {} + : number extends Key + ? {} + : symbol extends Key + ? {} + : Key extends PropertyKey + ? { [key in Key]: unknown } + : {}; + + /** + * Determines whether an object exists in another object's prototype chain. + * @param v Another object whose prototype chain is to be checked. + */ + isPrototypeOf(v: Object): boolean; + + /** + * Determines whether a specified property is enumerable. + * @param v A property name. + */ + propertyIsEnumerable(v: PropertyKey): boolean; +} + +/** + * Provides functionality common to all JavaScript objects. + */ +declare var Object: ObjectConstructor; + interface ObjectConstructor { new (value?: any): Object; (): {}; @@ -848,39 +895,41 @@ interface PropertyDescriptor { interface PropertyDescriptorMap { [key: PropertyKey]: PropertyDescriptor; -} - -interface Object { - /** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */ - constructor: Function; - - /** Returns a string representation of an object. */ - toString(): string; - - /** Returns a date converted to a string using the current locale. */ - toLocaleString(): string; - - /** Returns the primitive value of the specified object. */ - valueOf(): Object; - - /** - * Determines whether an object has a property with the specified name. - * @param v A property name. - */ - hasOwnProperty(v: PropertyKey): boolean; - - /** - * Determines whether an object exists in another object's prototype chain. - * @param v Another object whose prototype chain is to be checked. - */ - isPrototypeOf(v: Object): boolean; - - /** - * Determines whether a specified property is enumerable. - * @param v A property name. - */ - propertyIsEnumerable(v: PropertyKey): boolean; } +// +// +// interface Object { +// /** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */ +// constructor: Function; +// +// /** Returns a string representation of an object. */ +// toString(): string; +// +// /** Returns a date converted to a string using the current locale. */ +// toLocaleString(): string; +// +// /** Returns the primitive value of the specified object. */ +// valueOf(): Object; +// +// /** +// * Determines whether an object has a property with the specified name. +// * @param v A property name. +// */ +// hasOwnProperty(v: PropertyKey): boolean; +// +// /** +// * Determines whether an object exists in another object's prototype chain. +// * @param v Another object whose prototype chain is to be checked. +// */ +// isPrototypeOf(v: Object): boolean; +// +// /** +// * Determines whether a specified property is enumerable. +// * @param v A property name. +// */ +// propertyIsEnumerable(v: PropertyKey): boolean; +// } + // // // interface ObjectConstructor { @@ -994,13 +1043,15 @@ interface Object { // */ // keys(o: object): string[]; // } + +// +// +// /** +// * Provides functionality common to all JavaScript objects. +// */ +// declare var Object: ObjectConstructor; -/** - * Provides functionality common to all JavaScript objects. - */ -declare var Object: ObjectConstructor; - /** * Creates a new function. */ diff --git a/lib/lib.es2022.object.d.ts b/lib/lib.es2022.object.d.ts new file mode 100644 index 0000000..de07ea3 --- /dev/null +++ b/lib/lib.es2022.object.d.ts @@ -0,0 +1,19 @@ +interface ObjectConstructor { + /** + * Determines whether an object has a property with the specified name. + * @param o An object. + * @param v A property name. + */ + hasOwn( + o: object, + v: Key + ): o is string extends Key + ? {} + : number extends Key + ? {} + : symbol extends Key + ? {} + : Key extends PropertyKey + ? { [key in Key]: unknown } + : {}; +} diff --git a/lib/lib.es5.d.ts b/lib/lib.es5.d.ts index 7633a11..34d316d 100644 --- a/lib/lib.es5.d.ts +++ b/lib/lib.es5.d.ts @@ -15,6 +15,53 @@ type UnionToIntersection = ( */ declare function eval(x: string): unknown; +interface Object { + /** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */ + constructor: Function; + + /** Returns a string representation of an object. */ + toString(): string; + + /** Returns a date converted to a string using the current locale. */ + toLocaleString(): string; + + /** Returns the primitive value of the specified object. */ + valueOf(): Object; + + /** + * Determines whether an object has a property with the specified name. + * @param v A property name. + */ + hasOwnProperty( + v: Key + ): this is string extends Key + ? {} + : number extends Key + ? {} + : symbol extends Key + ? {} + : Key extends PropertyKey + ? { [key in Key]: unknown } + : {}; + + /** + * Determines whether an object exists in another object's prototype chain. + * @param v Another object whose prototype chain is to be checked. + */ + isPrototypeOf(v: Object): boolean; + + /** + * Determines whether a specified property is enumerable. + * @param v A property name. + */ + propertyIsEnumerable(v: PropertyKey): boolean; +} + +/** + * Provides functionality common to all JavaScript objects. + */ +declare var Object: ObjectConstructor; + interface ObjectConstructor { new (value?: any): Object; (): {}; diff --git a/tests/src/es2022.object.ts b/tests/src/es2022.object.ts new file mode 100644 index 0000000..aeb72b9 --- /dev/null +++ b/tests/src/es2022.object.ts @@ -0,0 +1,33 @@ +import { expectError, expectType } from "tsd"; + +// ObjectConstructor +{ + // https://github.com/uhyo/better-typescript-lib/issues/4 + const obj: object = {}; + if (Object.hasOwn(obj, "foo")) { + expectType(obj.foo); + expectType<{ foo: unknown }>(obj); + } + const obj2 = { foo: 123 }; + if (Object.hasOwn(obj2, "bar")) { + expectType(obj2.bar); + expectType<{ foo: number } & { bar: unknown }>(obj2); + } + const obj3 = () => {}; + if (Object.hasOwn(obj3, "baz")) { + expectType(obj3.baz); + expectType<(() => void) & { baz: unknown }>(obj3); + } + + const emptyObj = {}; + const key = Math.random() ? "foo" : "bar"; + if (Object.hasOwn(emptyObj, key)) { + expectError(emptyObj.foo); + expectError(emptyObj.bar); + expectType<{ foo: unknown } | { bar: unknown }>(emptyObj); + } + const key2: string = "123"; + if (Object.hasOwn(emptyObj, key2)) { + expectType<{}>(emptyObj); + } +} diff --git a/tests/src/es5.ts b/tests/src/es5.ts index 56c1158..52348f3 100644 --- a/tests/src/es5.ts +++ b/tests/src/es5.ts @@ -24,33 +24,68 @@ expectType<{ foo: number; bar: string }>( }) ); -// Object.defineProperty -expectType<{ foo: number }>( - Object.defineProperty({}, "foo", { - value: 123, - }) -); -expectType<{ foo: number } | { bar: number }>( - Object.defineProperty({}, "foo" as "foo" | "bar", { - value: 123, - }) -); +// Object +{ + // https://github.com/uhyo/better-typescript-lib/issues/4 + const obj: object = {}; + if (obj.hasOwnProperty("foo")) { + expectType(obj.foo); + expectType<{ foo: unknown }>(obj); + } + const obj2 = { foo: 123 }; + if (obj2.hasOwnProperty("bar")) { + expectType(obj2.bar); + expectType<{ foo: number } & { bar: unknown }>(obj2); + } + const obj3 = () => {}; + if (obj3.hasOwnProperty("baz")) { + expectType(obj3.baz); + expectType<(() => void) & { baz: unknown }>(obj3); + } -expectType<{ foo: number; bar: string; baz: boolean }>( - Object.defineProperties( - { foo: 123 }, - { - bar: { - value: "hi", - }, - baz: { - get() { - return true; + const emptyObj = {}; + const key = Math.random() ? "foo" : "bar"; + if (emptyObj.hasOwnProperty(key)) { + expectError(emptyObj.foo); + expectError(emptyObj.bar); + expectType<{ foo: unknown } | { bar: unknown }>(emptyObj); + } + const key2: string = "123"; + if (emptyObj.hasOwnProperty(key2)) { + expectType<{}>(emptyObj); + } +} + +// ObjectConstructor +{ + // Object.defineProperty + expectType<{ foo: number }>( + Object.defineProperty({}, "foo", { + value: 123, + }) + ); + expectType<{ foo: number } | { bar: number }>( + Object.defineProperty({}, "foo" as "foo" | "bar", { + value: 123, + }) + ); + + expectType<{ foo: number; bar: string; baz: boolean }>( + Object.defineProperties( + { foo: 123 }, + { + bar: { + value: "hi", }, - }, - } - ) -); + baz: { + get() { + return true; + }, + }, + } + ) + ); +} // CallableFunction {