Skip to content

When updating property from null to object "can't access property 'constructor'" is thrown #2372

@travis-sova

Description

@travis-sova

AI assistance used for creation of this issue

Description

Updating a node property from null to an object value using nodes.update() causes a TypeError in the selectiveNotDeepExtend, blocking updates when node properties are set to null.

Steps to Reproduce

  1. Create a network with a node that has a property set to null.
  2. Attempt to update that property to an object value using nodes.update().

Minimal Reproduction

import { DataSet, Network } from "vis-network/standalone";

const container = document.getElementById("mynetwork");

var nodes = new DataSet([
    { id: 1, label: "Node 1", test: null },
    { id: 2, label: "Node 2" },
    { id: 3, label: "Node 3" },
    { id: 4, label: "Node 4" },
    { id: 5, label: "Node 5" },
]);

var edges = new DataSet([
    { from: 1, to: 3 },
    { from: 1, to: 2 },
    { from: 2, to: 4 },
    { from: 2, to: 5 },
    { from: 3, to: 3 },
]);

const networkData = {
    nodes: nodes,
    edges: edges
}

const options = {}

const network = new Network(container, networkData, options);

nodes.update({
    id: 1,
    test: { x: 1, y: 2 }
})

Expected Behavior

The node updates successfully. The test property changes from null to { x: 1, y: 2 }.

Actual Behavior

The following error is thrown:

Uncaught TypeError: can't access property "constructor", a[prop] is null
    selectiveNotDeepExtend vis-network_standalone.js:7470
    parseOptions Node.js:344
    setOptions Node.js:170
    update NodesHandler.js:331
    update NodesHandler.js:29
    _trigger vis-network_standalone.js:14570
    _trigger vis-network_standalone.js:14569
    update vis-network_standalone.js:15042
    <anonymous> error.js:30
vis-network_standalone.js:7470:11

Root Cause

The selectiveNotDeepExtend function attempts to check if a property is an Object by accessing its constructor property:

function selectiveNotDeepExtend(propsToExclude, a, b) {
  // ...
  for (const prop in b) {
    // ...
    if (b[prop] && b[prop].constructor === Object) {
      if (a[prop] === void 0) {
        a[prop] = {};
      }
      // ERROR OCCURS HERE: a[prop] is null, but code tries to access a[prop].constructor
      if (a[prop].constructor === Object) {
        deepExtend(a[prop], b[prop]);
      } else {
        copyOrDelete(a, b, prop, allowDeletion);
      }
    }
    // ...
  }
  return a;
}

Only checks if a[prop] is undefined, not null. If a[prop] is null, a[prop].constructor === Object fails.

Workarounds

  • Remove and add the node again.
  • Get the node with nodes.get(id), mutate it directly, then call nodes.update() separately.
  • Avoid having null properties on nodes entirely.

Environment

  • vis-network version: 10.0.2 [standalone version]

Suggested Fix

Update the selectiveNotDeepExtend function to verify that a[prop] is neither null nor undefined before accessing its constructor property. For example:

if (b[prop] && b[prop].constructor === Object) {
  if (a[prop] === void 0) {
    a[prop] = {};
  }
  // Add null check here
  if (a[prop] !== null && a[prop].constructor === Object) {
    deepExtend(a[prop], b[prop]);
  } else {
    copyOrDelete(a, b, prop, allowDeletion);
  }
}

Alternatively, use optional chaining to safely check the constructor:

if (b[prop] && b[prop].constructor === Object) {
  if (a[prop] === void 0) {
    a[prop] = {};
  }
  if (a[prop]?.constructor === Object) {
    deepExtend(a[prop], b[prop]);
  } else {
    copyOrDelete(a, b, prop, allowDeletion);
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions