Connection checks

Connection checks restrict which connections (and therefore blocks) can connect to each other.

Connection checks are useful for modeling types. For example, the following three blocks have no business being connected, because they represent code that returns different types:

An empty list block, connected to a square root block, connected to an
uppercase block

Connection checks can be used to prevent these blocks from connecting. This gives users instantaneous feedback and prevents many simple mistakes.

How they work

Every connection can be associated with a "connection check" which is a nullable array of strings.

Two connections can connect if:

  1. They are compatible types (e.g. an output connecting to an input).
  2. They have at least one string in their connection check in common.

For example, connections with the following two checks could connect, because they share the 'apple' string:

['apple', 'ball', 'cat']
['apple', 'bear', 'caterpillar']

But connections with these two checks couldn't connect, because they don't share any strings:

['apple', 'ball', 'cat']
['ape', 'bear', 'caterpillar']

There is one other special case. If either array is null, then the two connections can also connect. This lets you define connections that can connect to anything.

null
['ape', 'bear', 'caterpillar]

Examples

For a list of examples of how to use connection checks, see the Connection check playbook.

Set checks

By default, all connections have a null connection-check, meaning they can connect to anything. Connection checks need to be assigned manually.

How you assign connection checks to connections is different depending on whether you're using JSON block definitions, or JavaScript block definitions.

JSON

For top-level connections, you assign the check directly to the property that defines the connection. The value you assign can be null, a string (which becomes the only entry in the connection check), or an array of strings.

{
  'type': 'custom_value_block',

  'output': 'a connection check entry',
},
{
  'type': 'custom_statement_block',

  'nextStatement': null, // null check
  'previousStatement': ['four', 'connection', 'check', 'entries']
}

For inputs, you can assign the check to a check property of the input definition. If the check property doesn't exist, the check is considered null. The value you assign can be a string, or an array of strings.

{
  'type': 'custom_block',
  'message0': '%1 %2',

  'args0': [
    {
      'type': 'input_value',
      'check': 'a connection check entry' // Accepts custom_value_block
    },
    {
      'type': 'input_statement',
      'check': ['two', 'entries'] // Accepts custom_statement_block
    }
  ]
}

JavaScript

For top-level connections, you can pass the check directly to the method that defines the connection. If you don't pass a value, the check is considered null. The value you pass can be a string (which becomes the only entry in the connection check), or an array of strings.

Blockly.Blocks['custom_value_block'] = {
  init: function() {
    this.setOutput(true, 'a connection check entry');
  }
};
Blockly.Blocks['custom_statement_block'] = {
  init: function() {
    this.setNextStatement(true); // null check
    this.setPreviousStatement(true, ['four', 'connection', 'check', 'entries']);
  }
};

For inputs, you can pass the check to the setCheck method, after you have defined the input. If the setCheck method isn't called, the check is considered null. The value you pass can be a string, or an array of strings.

Blockly.Blocks['custom_block'] = {
  init: function() {
    this.appendValueInput('NAME')
        .setCheck('a connection check entry'); // Accepts custom_value_block
    this.appendStatementInput('NAME')
        .setCheck(['two', 'entries']); // Accepts custom_statement_block
  }
};

Built-in check strings

The built-in blocks have connection checks with the values 'Array', 'Boolean', 'Colour', 'Number', and 'String'. If you want your blocks to interoperate with the built-in blocks, you can use these values to make them compatible.

Limitations

This system is quite robust and can solve many use-cases, but it does have a few limitations.

Restrict the greater context

This system does not, by itself, support restricting the "greater context" in which a connection is allowed to connect. For example, you cannot say that a break block is only allowed to exist inside of a loop block. The connection checking system only considers the immediate two connections being connected.

You can support this by using the event system to listen to block move events and check if the block is incorrectly positioned.

Blockly.Blocks['custom_block'] = {
  init: function() { }

  onchange: function(e) {
    if (this.workspace.isDragging()) return;
    if (e.type !== Blockly.Events.BlockMove) return;
    if (!this.getSurroundLoop()) this.outputConnection.disconnect();
  }

  loopTypes: new Set(); // Your valid *block types* (not connection checks).

  getSurroundLoop: function () {
    let block = this.getSurroundParent();
    do {
      if (loopTypes.has(block.type)) return block;
      block = block.getSurroundParent();
    } while (block);
    return null;
  },
}

Generic types

This system does not, by itself, support defining generic types. For example, you cannot create an "Identity" block, that "returns" whatever its input is.

You can somewhat support this by actively changing the connection check on the block's output to match its input. Which you can do using the event system to listen to block move events.

Blockly.Blocks['custom_block'] = {
  init: function() { }

  onchange: function(e) {
    if (e.type !== Blockly.Events.BlockMove) return;
    this.setOutput(
        true, this.getInputTargetBlock()?.outputConnection.getCheck());
  }
}

But if the connected block is also generic, then this doesn't work correctly. There is no good work around for this case.

Connection checkers

If this system doesn't work for your use case, you can also change how the connection checks are compared by creating a custom connection checker.

For example, if you wanted to create a more advanced system that handles some of the limitations of this one, you can create a custom connection checker.