---
title: User-Defined Types
description: Define your own typed structs with fields and methods. Construct with named fields, call methods, and mutate state with this. Model trading state in the shape of the problem.
---

<div class="flex gap-3 mb-6">
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-purple-50 text-purple-600 text-sm font-medium">
    Advanced
  </span>
  <span class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-gray-100 text-gray-600 text-sm font-medium">
    9 min read
  </span>
</div>

## Introduction

A `type` is a struct you define: a named bundle of fields, optionally with methods that operate on those fields. Instead of tracking a supply zone as three loose parallel arrays (`zoneTops`, `zoneBottoms`, `zoneTouches`), you describe it once as a `Zone` and work with whole zones. State takes the shape of the problem.

```javascript
type Zone {
  top: number
  bottom: number
  touches: number
}
```
Each field is declared with a name and a type. Once a type exists, you create instances of it, read and write their fields, and pass them around like any other value.

## Construct with named fields

You build an instance with `.new(...)`, naming each field you set:

```javascript
var z = Zone.new(top=d.high, bottom=d.low, touches=0)
var h = z.top - z.bottom
```

Named-field construction is order-independent and self-documenting: there is no "which positional argument was `bottom` again?" The constructor only accepts fields the type actually declares. Pass a field the type does not have and it is a compile error, not a silent extra property, so a typo is caught before the script ever runs.
## Add behavior with methods

A type can carry methods. Declare them with `func` **inside** the type block, and they can read and mutate the instance through `this`:

```javascript
type Zone {
  top: number
  bottom: number
  touches: number

  func height() {
    return this.top - this.bottom
  }

  func registerTouch(price) {
    if (price >= this.bottom && price <= this.top) {
      this.touches += 1
      return true
    }
    return false
  }
}
```

Now the data and the logic that maintains it live together. `height()` derives a value from the fields. `registerTouch(price)` is a self-updating operation: ask the zone whether the current price touched it, and it answers while bumping its own `touches` counter. The zone manages its own state.
```javascript
var inZone = z.registerTouch(d.close)   // true/false, && z.touches updates itself
var tall   = z.height()
```



This is why user-defined types matter for trading scripts. A "supply zone that counts its own touches and retires after the third," an "order block that tracks whether price mitigated it," a "trailing-stop level that ratchets": each becomes one type whose methods enforce its rules, instead of bookkeeping smeared across the whole script. Hold these in a `var` to track one, or in an array (see [Collections](/kscript/core-concepts/collections)) to manage a live set of them across bars.
## A complete typed struct

This script declares `Zone` with both methods, constructs it with named fields, calls `height()` and `registerTouch()`, and lets the method mutate `touches` through `this`.

```javascript title="scripts/probes/lang-types/struct_methods.ks" lines wrap
//@version=2
define(title="Verified Struct Methods", position="offchart", axis=true)

type Zone {
  top: number
  bottom: number
  touches: number

  func height() {
    return this.top - this.bottom
  }

  func registerTouch(price) {
    if (price >= this.bottom && price <= this.top) {
      this.touches += 1
      return true
    }
    return false
  }
}

timeseries trade = ohlcv(symbol=currentSymbol, exchange=currentExchange)

var top = trade.high
var bottom = trade.low
var closeNow = trade.close
var zone = Zone.new(top=top, bottom=bottom, touches=0)
var touched = zone.registerTouch(closeNow)
var score = zone.height() + zone.touches + (touched ? 1 : 0) + barIndex

plotLine(value=score, colors=["#2563eb"], label=["Zone score"], desc=["struct method score"])
```


**What to expect:** `zone.height()` returns the high-minus-low band width, `registerTouch(closeNow)` reports whether the close fell inside the band and increments `zone.touches` when it did, and `zone.touches` reads the running count back. The whole zone is one value carrying its own data and behavior.

## Rules and gotchas

A few constraints the compiler enforces. Each fails at compile time with a precise line and column, so you find out immediately.

**Constructors reject unknown fields.** `.new(...)` only accepts fields the type declares. Passing one it does not have, such as `Zone.new(top=..., bottom=..., label=...)`, fails with:

```
Unknown field 'label' for type 'Zone'
```

**`this` only works inside methods.** It refers to the current instance, so it is meaningless outside a type's own `func`. Using it at the top level fails with:

```
'this' can only be used inside type methods
```

**`__type` and `__kscriptType` are reserved field names.** The engine uses them internally to tag instances, so you cannot declare a field with either name. Doing so fails with `reserved field name '__type'` or `reserved field name '__kscriptType'`. Any other field name is yours to use.

{% hint style="info" %}
Types are part of the broader v3 type system. For how `na`, scalars, and the built-in series types fit together, see the [Type System](/kscript/core-concepts/type-system) overview.
{% endhint %}
