Skip to main content

Testing class compatibility with isAssignableFrom

· 4 min read
Simon Porritt
VisuallyJs Development
info

This post is from JsPlumb, which is now in maintenance mode. We still use this code in VisuallyJs though.

Classes are a controversial topic in the Javascript/Typescript world. Are they a terrible idea? Are they really useful, when used sensibly? This post will make no attempt at answering either of these questions. This post is about a niche method that I first encountered many years ago in the world of Java - isAssignableFrom - and how you can go about writing it for use in Typescript/Javascript.

What does it do?

This is what the Javadocs have to say about it:

public boolean isAssignableFrom(Class<?> cls)

Determines if the class or interface represented by this Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified Class parameter. It returns true if so; otherwise it returns false.

So, given some class, you can use isAssignableFrom to figure out whether that class is a subclass of some other class.

Why would I need this?

If you're thinking it's a bit niche, yes, I agree. isAssignableFrom is one of those methods you don't use much. But when you need it, you need it.

JsPlumb offers the concept of Decorators - a class that can add arbitrary extra content to the UI after a layout has been run. These can be used for a great variety of things, and if you're a JsPlumb user but haven't yet come across them, we'd encourage you to check them out.

You can register a decorator on JsPlumb using one of two different syntaxes - you can create an extension of Decorator and register it:

import { Decorator, Decorators, DecorateParams, DecorateResetParams } from "@jsplumbtoolkit/browser-ui"

class MyDecorator extends Decorator {

decorate(params: DecorateParams): void {
...
}

reset(params: DecorateResetParams): void {

}
}

Decorators.register("MY_DECORATOR", MyDecorator)

...or you can just register an function whose shape fits the bill (specifically, it matches the IDecorator interface):

import { Decorators, DecorateParams, DecorateResetParams } from "@jsplumbtoolkit/browser-ui"


Decorators.register("MY_DECORATOR", function() {

this.decorate = (params: DecorateParams): void => {
...
}

this.reset = (params: DecorateResetParams): void => {

}
})

The register method has this signature:

register:(name:string, dec:Constructable<Decorator>)
info

Constructable<Decorator> means "a function which, when invoked with new..., will return an instance of Decorator.". We use this interface in a few places in the JsPlumb code.

export type Constructable<T> = { new(...args: any[]): T }

So the question is, how does register know what was passed in? Was it given a Decorator, or was it given some other function? isAssignableFrom to the rescue!


const decoratorMap:Record<string, Constructable<Decorator>> = {}

register:(name:string, dec:Constructable<Decorator>) => {

if (isAssignableFrom(dec, Decorator)) {
decoratorMap[name] = dec
} else {
decoratorMap[name] = functionalDecorator(dec)
}
}

The register method determines whether or not it's been given a Decorator and just stashes it if so. Otherwise it uses the helper method functionalDecorator to wrap what it was given (the details of that method are orthogonal to this discussion).

How does it work then?

Show Me The Code

It's a simple method:

export function isAssignableFrom(object:any, cls:any) {
let proto = object.prototype
while (proto != null) {
if (proto instanceof cls) {
return true
}
proto = proto.prototype
}
return false
}

It basically walks the prototype chain and tests each prototype via an instanceof test.

Isn't this just instanceof?

No! It may seem like it is, but the key here is that comparison is being made on a class, not on an object. We want to know when someone has passed a reference to a class into register.

Consider this console session:


class Foo {}
class Bar extends Foo {}

const bar = new Bar()

bar instanceof Bar
// --> true

bar instanceof Foo
// --> true

So far, so good: our instance of Bar, which is a subclass of Foo, is recognised as being both an instance of Foo and of Bar. But how about this:

Bar instanceof Foo
// --> false

which makes sense really, because Bar is not an instance of anything - it's a class.

If we want to check whether we can consider Bar as Foo we're going to need our isAssignableFrom function:

isAssignableFrom(Bar, Foo)
// --> true


Try VisuallyJs

VisuallyJs offers an extensive list of starter apps, diagrams, charts and dashboards to quick start your development, in React, Angular, Vue, Svelte and Typescript/Javascript.

VisuallyJs - fully featured alternative to ReactFlow and ngDiagram
Call Flow
Use VisuallyJs to build a visual Call Flow editor
VisuallyJs - build diagrams and rich visual UIs fast
AI Agent Builder
Use VisuallyJs to create an advanced AI agent builder
VisuallyJs - leading alternative to GoJS, JointJS, ReactFlow and SvelteFlow
Chatbot
Use VisuallyJs to build a Chatbot editor, with actions, messages, input and choices
VisuallyJs - leading alternative to GoJS, JointJS, ReactFlow and SvelteFlow
Flowchart
Fully featured flowchart builder including support for custom shapes, edge routing to avoid vertices, shape resize/rotate, SVG/PNG/JPG export and more
VisuallyJs - leading alternative to GoJS, JointJS, ReactFlow and SvelteFlow
BPMN
BPMN editor for modelling the steps of a business process. Pools, lanes, and a full set of task, event and gateway types
VisuallyJs - leading alternative to GoJS, JointJS, ReactFlow and SvelteFlow
ERD
ERD editor for modelling the steps of a business process
VisuallyJs - industry standard diagramming and rich visual UI Javascript and Typescript library
Gantt
Interactive Gantt chart featuring tasks, task groups and milestones
VisuallyJs - flowcharts, AI Agent builders, chatbots, bar charts, decision trees, mindmaps, org charts and more
Kanban
Fully featured Kanban board. Drag items between columns and use the inspector to update items and columns
When you've reached the limits with ReactFlow, VisuallyJs has what you need
Database Schema
Database Schema builder with support for tables, views, multiple columns types, and column relationships
VisuallyJs - build diagrams and rich visual UIs fast
Org Chart
Uses the classic org chart layout and provides an inspector from which the user can navigate around
VisuallyJs - leading alternative to GoJS, JointJS, ReactFlow and SvelteFlow
Circuit Diagram
Fully featured starter app containing a circuit diagram builder
VisuallyJs - flowcharts, AI Agent builders, chatbots, bar charts, decision trees, mindmaps, org charts and more
Scada/HMI
Professional and modern Scada/HMI application with fluid, heating/cooling & instrumentation shapes, adhering to the HMI ISA-101 Design Standard
VisuallyJs - leading alternative to GoJS, JointJS, ReactFlow and SvelteFlow
Mindmap
The mindmap builder highlights several advanced features, such as custom layouts, parsers and exporters
VisuallyJs - JavaScript and Typescript diagramming library that fuels exceptional UIs
Neighbourhood Views
Demonstrates how to include multiple views of a dataset on one page
VisuallyJs - callflow builders, sankey charts, database schemas, ERD diagrams and more
Logic Gates
Fully featured starter app containing a logic gates diagram builder
VisuallyJs - flowcharts, AI Agent builders, chatbots, bar charts, decision trees, mindmaps, org charts and more
Template
Basic starter app demonstrating how to setup VisuallyJs and its main features
VisuallyJs - JavaScript and Typescript diagramming library that fuels exceptional UIs
Area & Line charts
Use VisuallyJs to create area and line charts
When you've reached the limits with SvelteFlow, VisuallyJs has what you need
Bar & Column charts
Multiple series, stacked, grouped, pivoted, and much more
When you've reached the limits with ngDiagram, VisuallyJs has what you need
Scatter & Bubble charts
Circle, rectangle, triangle or custom markers, multiple series, fully customizable
When you've reached the limits with SvelteFlow, VisuallyJs has what you need
Sankey chart
Use VisuallyJs to create a professional Sankey chart, with support for pivoting
VisuallyJs - leading alternative to GoJS, JointJS, ReactFlow and SvelteFlow
Supply Chain Analyzer
Dashboard for managing and analyzing supply chains
VisuallyJs - JavaScript and Typescript diagramming library that fuels exceptional UIs
Network Infrastructure
Combine a network management diagram with charts showing projected cost and resource usage
VisuallyJs - build diagrams and rich visual UIs fast
Scrolling Lists
Use the ListManager plugin to manage scrolling lists: as elements are scrolled out of the view, their edges are moved to the list container
VisuallyJs - industry standard diagramming and rich visual UI Javascript and Typescript library
FIFA World Cup
A visualizer for the FIFA World cup - group stages, team journeys and a tournament view.
When you've reached the limits with ReactFlow, VisuallyJs has what you need
Fault Tree Analysis
Combines a fault tree analysis diagram with charts showing risk and list of cut sets