Skip to content

Type System

Graphyn uses a WorkflowType sealed interface to describe what kind of data flows through a port. The type system is checked when a user draws a connection — incompatible types are rejected with a visual toast.


Type variants

sealed interface WorkflowType {
    data object StringType  : WorkflowType
    data object IntType     : WorkflowType
    data object DoubleType  : WorkflowType
    data object BooleanType : WorkflowType
    data object OpaqueType  : WorkflowType          // accepts any incoming type

    data class ListType(val elementType: WorkflowType) : WorkflowType
    data class NullableType(val wrappedType: WorkflowType) : WorkflowType
    data class RecordType(val fields: Map<String, WorkflowType>) : WorkflowType
    data class EnumType(val values: List<String>) : WorkflowType
    data class MultiEnumType(val values: List<String>) : WorkflowType
}

Primitive types

Type Kotlin equivalent Example value
StringType String WorkflowValue.StringValue("hello")
IntType Int WorkflowValue.IntValue(42)
DoubleType Double WorkflowValue.DoubleValue(3.14)
BooleanType Boolean WorkflowValue.BooleanValue(true)

Structural types

ListType(elementType) — a homogeneous list.

WorkflowType.ListType(WorkflowType.StringType)   // List<String>
WorkflowType.ListType(WorkflowType.OpaqueType)   // List<Any>

RecordType(fields) — a named map of typed fields, similar to a JSON object or a data class.

WorkflowType.RecordType(mapOf(
    "name"  to WorkflowType.StringType,
    "score" to WorkflowType.DoubleType,
))

NullableType(wrappedType) — wraps any type to indicate the value may be absent.

WorkflowType.NullableType(WorkflowType.StringType)  // String?

In the FieldCard UI, nullable ports render a checkbox that toggles between null and the inner type's default value.

EnumType(values) — a single-choice dropdown from a fixed list of string options.

WorkflowType.EnumType(listOf("GET", "POST", "PUT", "DELETE"))

MultiEnumType(values) — a multi-choice variant of EnumType.

OpaqueType

OpaqueType is a structural wildcard — it accepts an incoming connection of any type. Use it for pass-through ports, union operations, or when the concrete type is determined at runtime.

PortSpec("value", WorkflowType.OpaqueType)  // accepts String, Int, Record, List, etc.

Only use OpaqueType when the node genuinely cannot know the type in advance. Over-using it defeats type safety and removes all editor feedback for the user.


Compatibility rules

A connection from output port O to input port I is accepted when:

Input port type Accepts
OpaqueType any output type
StringType StringType only
IntType IntType only
DoubleType DoubleType only
BooleanType BooleanType only
ListType(E) ListType(E) where E matches
NullableType(T) T or NullableType(T)
RecordType(F) RecordType(F) with same field names and matching field types
EnumType(V) EnumType(V) with same values list

Type checking runs in WorkflowTypeCompatibility.isCompatible(outputType, inputType) in the core module.


displayName() extension

fun WorkflowType.displayName(): String

Returns a human-readable label shown in port tooltips and the inspector:

Type displayName()
StringType "String"
IntType "Int"
ListType(StringType) "List<String>"
NullableType(IntType) "Int?"
RecordType(...) "Record"
OpaqueType "Any"

WorkflowValue

Every port value at runtime is a WorkflowValue:

sealed interface WorkflowValue {
    data class StringValue(val value: String)    : WorkflowValue
    data class IntValue(val value: Int)          : WorkflowValue
    data class DoubleValue(val value: Double)    : WorkflowValue
    data class BooleanValue(val value: Boolean)  : WorkflowValue
    data class ListValue(val items: List<WorkflowValue>) : WorkflowValue
    data class RecordValue(val fields: Map<String, WorkflowValue>) : WorkflowValue
    data object NullValue : WorkflowValue
}

NullValue is the runtime representation of a NullableType port with no value set.