diff --git a/src/promise/src/Shared/Promise.lua b/src/promise/src/Shared/Promise.lua index 4af15a465e9..aaec0366182 100644 --- a/src/promise/src/Shared/Promise.lua +++ b/src/promise/src/Shared/Promise.lua @@ -41,7 +41,14 @@ export type Promise = typeof(setmetatable( @return boolean ]=] function Promise.isPromise(value: any): boolean - return type(value) == "table" and value.ClassName == "Promise" + if type(value) ~= "table" then + return false + end + + -- Read the metatable directly: indexing the value runs its __index, and some objects error on + -- unknown keys. Promises from another copy of this package have their own metatable. + local metatable = getmetatable(value) + return type(metatable) == "table" and rawget(metatable, "ClassName") == "Promise" end --[=[ diff --git a/src/promise/src/Shared/Promise.spec.lua b/src/promise/src/Shared/Promise.spec.lua index c5fc988953f..677dc46628a 100644 --- a/src/promise/src/Shared/Promise.spec.lua +++ b/src/promise/src/Shared/Promise.spec.lua @@ -26,6 +26,23 @@ describe("Promise.isPromise", function() expect(Promise.isPromise({})).toEqual(false) expect(Promise.isPromise({ ClassName = "NotAPromise" })).toEqual(false) end) + + it("returns false without indexing objects that error on unknown keys", function() + local strict = setmetatable({}, { + __index = function(_, key) + error(string.format("%s is not a valid member", tostring(key))) + end, + }) + + expect(Promise.isPromise(strict)).toEqual(false) + end) + + it("returns true for a promise from another copy of the package", function() + local OtherPromise = { ClassName = "Promise" } + OtherPromise.__index = OtherPromise + + expect(Promise.isPromise(setmetatable({}, OtherPromise))).toEqual(true) + end) end) describe("Promise.new", function()