fix(reactivity): support custom Symbol.toStringTag for collections

This commit is contained in:
Özer Gökalpsezer 2025-02-10 10:41:26 +03:00
parent d9444e5523
commit b42d264da0
2 changed files with 47 additions and 6 deletions

View File

@ -130,6 +130,35 @@ describe('reactivity/reactive', () => {
expect(dummy).toBe(2)
})
test('custom collection type with custom Symbol.toStringTag is handled as a collection', () => {
class MyCustomMap extends Map {
get [Symbol.toStringTag]() {
return 'MyCustomMap'
}
}
const myCustomMap = new MyCustomMap()
expect(Object.prototype.toString.call(myCustomMap)).toBe(
'[object MyCustomMap]',
)
const observed = reactive(myCustomMap)
expect(isReactive(observed)).toBe(true)
expect(isProxy(observed)).toBe(true)
let dummy: boolean = false
effect(() => {
dummy = observed.has('foo')
})
expect(dummy).toBe(false)
observed.set('foo', 'bar')
expect(dummy).toBe(true)
})
test('observed value should proxy mutations to original (Object)', () => {
const original: any = { foo: 1 }
const observed = reactive(original)

View File

@ -68,14 +68,26 @@ function getTargetType(value: Target) {
if (value[ReactiveFlags.SKIP] || !Object.isExtensible(value)) {
return TargetType.INVALID
}
const type = targetTypeMap(toRawType(value))
let type = targetTypeMap(rawType)
// If we got INVALID but the value is actually a plain object (even if its raw type was changed
// by a custom Symbol.toStringTag), then force it to be reactive.
if (type === TargetType.INVALID && isPlainObject(value)) {
type = TargetType.COMMON
let type = targetTypeMap(toRawType(value))
// If the raw type mapping fails, we add extra checks:
if (type === TargetType.INVALID) {
// Check for collection types even if they have a custom Symbol.toStringTag.
if (
value instanceof Map ||
value instanceof Set ||
value instanceof WeakMap ||
value instanceof WeakSet
) {
type = TargetType.COLLECTION
}
// Check if the value is a plain object despite a custom tag.
else if (isPlainObject(value)) {
type = TargetType.COMMON
}
}
return type
}