diff --git a/multipart/formfile.ts b/multipart/formfile.ts index 592f4e529b8f..a0e721a15fc4 100644 --- a/multipart/formfile.ts +++ b/multipart/formfile.ts @@ -1,4 +1,5 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { hasOwnProperty } from "../util/has_own_property.ts"; /** FormFile object */ export interface FormFile { @@ -19,9 +20,5 @@ export interface FormFile { /** Type guard for FormFile */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function isFormFile(x: any): x is FormFile { - return ( - typeof x === "object" && - x.hasOwnProperty("filename") && - x.hasOwnProperty("type") - ); + return hasOwnProperty(x, "filename") && hasOwnProperty(x, "type"); } diff --git a/multipart/formfile_test.ts b/multipart/formfile_test.ts index 63509fc9d2b5..52dd3addda69 100644 --- a/multipart/formfile_test.ts +++ b/multipart/formfile_test.ts @@ -18,3 +18,15 @@ test(function multipartIsFormFile(): void { false ); }); + +test(function isFormFileShouldNotThrow(): void { + assertEquals( + isFormFile({ + filename: "foo", + type: "application/json", + hasOwnProperty: "bar" + }), + true + ); + assertEquals(isFormFile(Object.create(null)), false); +}); diff --git a/util/has_own_property.ts b/util/has_own_property.ts new file mode 100644 index 000000000000..707d951d3b33 --- /dev/null +++ b/util/has_own_property.ts @@ -0,0 +1,30 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. + +/** + * Determines whether an object has a property with the specified name. + * Avoid calling prototype builtin `hasOwnProperty` for two reasons: + * + * 1. `hasOwnProperty` is defined on the object as something else: + * + * const options = { + * ending: 'utf8', + * hasOwnProperty: 'foo' + * }; + * options.hasOwnProperty('ending') // throws a TypeError + * + * 2. The object doesn't inherit from `Object.prototype`: + * + * const options = Object.create(null); + * options.ending = 'utf8'; + * options.hasOwnProperty('ending'); // throws a TypeError + * + * @param obj A Object. + * @param v A property name. + * @see https://eslint.org/docs/rules/no-prototype-builtins + */ +export function hasOwnProperty(obj: T, v: PropertyKey): boolean { + if (obj == null) { + return false; + } + return Object.prototype.hasOwnProperty.call(obj, v); +} diff --git a/ws/mod.ts b/ws/mod.ts index df8cab01f8ec..b649ef1781a2 100644 --- a/ws/mod.ts +++ b/ws/mod.ts @@ -1,6 +1,7 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. import { decode, encode } from "../strings/mod.ts"; +import { hasOwnProperty } from "../util/has_own_property.ts"; type Conn = Deno.Conn; type Writer = Deno.Writer; @@ -34,7 +35,7 @@ export interface WebSocketCloseEvent { export function isWebSocketCloseEvent( a: WebSocketEvent ): a is WebSocketCloseEvent { - return typeof a === "object" && a.hasOwnProperty("code"); + return hasOwnProperty(a, "code"); } export type WebSocketPingEvent = ["ping", Uint8Array];