<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Léo Sarrazin</title>
    <description>The latest articles on DEV Community by Léo Sarrazin (@lsarrazi).</description>
    <link>https://dev.to/lsarrazi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1091119%2F8843ae4c-ac7a-4260-8fd5-655bf5a26f30.png</url>
      <title>DEV Community: Léo Sarrazin</title>
      <link>https://dev.to/lsarrazi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lsarrazi"/>
    <language>en</language>
    <item>
      <title>Trait Views: exposing behavior in JavaScript without inheritance</title>
      <dc:creator>Léo Sarrazin</dc:creator>
      <pubDate>Wed, 14 Jan 2026 18:06:19 +0000</pubDate>
      <link>https://dev.to/lsarrazi/trait-views-exposing-behavior-in-javascript-without-inheritance-d6l</link>
      <guid>https://dev.to/lsarrazi/trait-views-exposing-behavior-in-javascript-without-inheritance-d6l</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;JavaScript gives us many ways to share behavior: inheritance, mixins, composition, interfaces (via TypeScript).&lt;br&gt;
Yet in larger systems, all of them tend to fall short in one way or another.&lt;/p&gt;

&lt;p&gt;Inheritance is rigid.&lt;br&gt;
Mixins tend to leak state.&lt;br&gt;
Passing raw objects exposes too much surface area.&lt;/p&gt;

&lt;p&gt;What JavaScript lacks is a way to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Give me a &lt;strong&gt;view&lt;/strong&gt; of this object, exposing a specific capability, with default behavior — without changing the object itself.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article introduces &lt;strong&gt;Trait Views&lt;/strong&gt;: a runtime pattern inspired by Rust traits, adapted to JavaScript’s object model.&lt;/p&gt;

&lt;p&gt;This is not a language proposal, and not a replacement for existing patterns.&lt;br&gt;
It is an exploration of a missing abstraction.&lt;/p&gt;


&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Consider a simple situation: you have an object, and you want to treat it as &lt;em&gt;observable&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Not by inheriting from an &lt;code&gt;Observable&lt;/code&gt; base class.&lt;br&gt;
Not by mixing methods into it.&lt;br&gt;
Not by passing the object itself and trusting everyone to “do the right thing”.&lt;/p&gt;

&lt;p&gt;You want to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“For this part of the system, this object should be seen &lt;em&gt;only&lt;/em&gt; as observable.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;JavaScript does not give us a native way to express that.&lt;/p&gt;


&lt;h2&gt;
  
  
  Trait Views — the idea
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Trait View&lt;/strong&gt; is a derived object that exposes a specific behavior of another object.&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not the original object&lt;/li&gt;
&lt;li&gt;not a copy of it&lt;/li&gt;
&lt;li&gt;not a mixin applied to it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a &lt;strong&gt;projection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You don’t &lt;em&gt;add&lt;/em&gt; a trait to an object.&lt;br&gt;
You &lt;em&gt;derive a view&lt;/em&gt; of that object.&lt;/p&gt;


&lt;h2&gt;
  
  
  A minimal example: Observable
&lt;/h2&gt;

&lt;p&gt;Let’s start with something deliberately simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Note this one is optional&lt;/span&gt;
  &lt;span class="nx"&gt;observeTimesTwo&lt;/span&gt;&lt;span class="p"&gt;?():&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This trait defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one core behavior: &lt;code&gt;observe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;one default behavior built on top of it: &lt;code&gt;observeTwice&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, a completely unrelated object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sensor&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;

  &lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;implements Observable&lt;/code&gt; clause is important.&lt;/p&gt;

&lt;p&gt;Even though &lt;code&gt;Observable&lt;/code&gt; is never extended, TypeScript still enforces that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all required methods are present&lt;/li&gt;
&lt;li&gt;method signatures are compatible&lt;/li&gt;
&lt;li&gt;refactors remain type-safe&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means Trait Views are not “duck typing in the dark”.&lt;br&gt;
They are &lt;strong&gt;structurally typed and checked at compile time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sensor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sensor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sensor&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;observeTimesTwo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// 42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happened here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Sensor&lt;/code&gt; remains unchanged.&lt;/li&gt;
&lt;li&gt;No methods were copied onto it.&lt;/li&gt;
&lt;li&gt;No inheritance was introduced.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, &lt;code&gt;Observable.from(sensor)&lt;/code&gt; created a &lt;strong&gt;strongly typed view&lt;/strong&gt; of &lt;code&gt;sensor&lt;/code&gt; that exposes observable behavior, including default logic that the original object never had.&lt;/p&gt;

&lt;p&gt;The original object is not observable. The &lt;em&gt;view&lt;/em&gt; is.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stateless vs Stateful Trait Views
&lt;/h2&gt;

&lt;p&gt;Trait Views can exist in two modes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stateless &lt;code&gt;Observable.from(..) -&amp;gt; Stateless&amp;lt;Observable&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A stateless trait view (the default mode):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;does not own any state&lt;/li&gt;
&lt;li&gt;its own constructor is intentionally unused and never called&lt;/li&gt;
&lt;li&gt;is cached per instance and is frozen for stability&lt;/li&gt;
&lt;li&gt;delegates all behavior back to the original object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From a TypeScript perspective, this means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all trait methods are guaranteed to exist&lt;/li&gt;
&lt;li&gt;trait-owned properties remain &lt;em&gt;optional*&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conceptually, this is close to a borrowed trait object in Rust (&lt;code&gt;&amp;amp;dyn Trait&lt;/code&gt;): a stable interface over existing state.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;*In stateless mode, trait-owned properties are intentionally typed as optional. This anticipates cases where an implementing object may expose a getter, in which case deleting trait state would be incorrect as it would get rid of getters too.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Stateful &lt;code&gt;Observable.from(..) -&amp;gt; Stateful&amp;lt;Observable&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A stateful trait view:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;owns its own internal state&lt;/li&gt;
&lt;li&gt;have its own constructor and parameters&lt;/li&gt;
&lt;li&gt;is explicitly constructed via &lt;code&gt;Observable.from(object, param1, param2, ...)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;is not cached&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From a typing perspective:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all trait methods exist&lt;/li&gt;
&lt;li&gt;all trait properties are guaranteed to be present&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows traits to carry state without polluting the original object.&lt;/p&gt;

&lt;p&gt;Both modes exist because they solve different problems. &lt;br&gt;
A mode can be chosen for a specific trait using an option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyStatefulTrait&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MyStatefulTrait&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stateful&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;myState&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myParameter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Trait Views as capability boundaries
&lt;/h2&gt;

&lt;p&gt;So far, Trait Views look like a way to share behavior.&lt;/p&gt;

&lt;p&gt;But they do something else that is just as important: &lt;strong&gt;they reduce surface area&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Disposable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Disposable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;Disposable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;do not touch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;dangerousOperation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;disposable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Disposable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;disposable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;            &lt;span class="c1"&gt;// OK&lt;/span&gt;
&lt;span class="nx"&gt;disposable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dangerousOperation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// ❌ not accessible&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trait view exposes &lt;strong&gt;only one capability: disposal&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The original object may have many methods, many states, many invariants —&lt;br&gt;
but the view deliberately restricts what is visible.&lt;/p&gt;

&lt;p&gt;Instead of passing objects around, you pass &lt;em&gt;what they are allowed to do&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Trait Views are not just about reuse.&lt;br&gt;
They are about &lt;strong&gt;encapsulation by projection&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How this works (conceptually)
&lt;/h2&gt;

&lt;p&gt;At a high level, a trait view:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uses the trait prototype for default behavior&lt;/li&gt;
&lt;li&gt;binds overridden methods to the original object&lt;/li&gt;
&lt;li&gt;optionally binds accessors&lt;/li&gt;
&lt;li&gt;caches stateless views (weakly, so they do not prevent garbage collection)&lt;/li&gt;
&lt;li&gt;freezes stateless views for stability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The original object is never mutated.&lt;br&gt;
The trait view is a separate object with a clearly defined surface.&lt;/p&gt;




&lt;h2&gt;
  
  
  How this compares to Rust traits
&lt;/h2&gt;

&lt;p&gt;Trait Views are inspired by Rust traits, but they are not the same thing.&lt;/p&gt;

&lt;p&gt;Similarities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;behavior-oriented abstraction&lt;/li&gt;
&lt;li&gt;default methods&lt;/li&gt;
&lt;li&gt;dynamic dispatch through a stable interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;resolution is runtime, not compile-time&lt;/li&gt;
&lt;li&gt;there are no coherence or orphan rules&lt;/li&gt;
&lt;li&gt;overrides are name-based&lt;/li&gt;
&lt;li&gt;TypeScript cannot express all guarantees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a flaw of the pattern.&lt;br&gt;
It is a consequence of JavaScript’s dynamic nature.&lt;/p&gt;

&lt;p&gt;Trait Views aim for &lt;strong&gt;similar ergonomics&lt;/strong&gt;, not identical semantics.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why not just bind functions?
&lt;/h2&gt;

&lt;p&gt;At first glance, a Trait View may look trivial.&lt;br&gt;
After all, one could create a new object and bind a few methods manually.&lt;br&gt;
The difference is not in what happens at runtime — it is in what is being modeled.&lt;/p&gt;

&lt;p&gt;Trait Views provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a consistent abstraction&lt;/li&gt;
&lt;li&gt;a clear boundary between object state and trait behavior&lt;/li&gt;
&lt;li&gt;a stable, typed surface&lt;/li&gt;
&lt;li&gt;optional caching and freezing guarantees&lt;/li&gt;
&lt;li&gt;a shared mental model across a codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manually binding functions solves a local problem.&lt;br&gt;
Trait Views aim to solve a systemic one.&lt;br&gt;
They are less about convenience, and more about expressing intent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparison with existing libraries
&lt;/h2&gt;

&lt;p&gt;Several libraries already explore the idea of traits in JavaScript and TypeScript.&lt;br&gt;
Trait Views are not meant to replace them — they are designed with a different focus.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;@traits-ts/core&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@traits-ts/core&lt;/code&gt; is a library for TypeScript that provides a trait (or mixin) facility to extend classes with multiple base functionalities, even though JavaScript does not natively allow multiple inheritance.&lt;/p&gt;

&lt;p&gt;It leverages TypeScript’s type system and the regular class extends mechanism to combine trait behaviors into new class hierarchies with compile-time type safety. This makes it well-suited for static composition, where traits are known and applied as part of the type definition.&lt;/p&gt;

&lt;p&gt;Trait Views explore a different space: they focus on runtime adaptation and per-instance views of existing objects, rather than static composition of classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;traits.js&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;traits.js&lt;/code&gt; is a dedicated JavaScript library for trait composition, building on a classic definition of traits as reusable units of behavior.&lt;/p&gt;

&lt;p&gt;It provides a way to compose zero or more traits into a single composite trait and then use that to construct objects with the combined behavior. This model emphasizes combining behavior into new objects as part of object construction.&lt;/p&gt;

&lt;p&gt;Trait Views take a different perspective: instead of composing traits into objects, they derive objects from traits, creating views over existing objects without mutating them.&lt;/p&gt;

&lt;p&gt;The difference is less about capability, and more about direction and use case.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tradeoffs and limitations
&lt;/h2&gt;

&lt;p&gt;Trait Views are not free.&lt;/p&gt;

&lt;p&gt;They rely on runtime reflection.&lt;br&gt;
They assume that getters are side-effect free.&lt;br&gt;
They cannot prevent all forms of monkey patching.&lt;br&gt;
They require discipline in API design.&lt;/p&gt;

&lt;p&gt;This pattern is not meant for everything. Trait Views are particularly well suited for engines and simulations, ECS-style architectures, capability-based APIs or systems where surface control matters.&lt;/p&gt;

&lt;p&gt;They are not ideal for simple CRUD applications or UI-heavy codebases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion — and an open question
&lt;/h2&gt;

&lt;p&gt;Trait Views are not a new language feature.&lt;br&gt;
They are a pattern — a way to think differently about behavior in JavaScript.&lt;/p&gt;

&lt;p&gt;They sit somewhere between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rust-style traits&lt;/li&gt;
&lt;li&gt;capability-based design&lt;/li&gt;
&lt;li&gt;runtime object views&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this stage, Trait Views are an experiment. &lt;a href="https://github.com/lsarrazi/trait-views-article" rel="noopener noreferrer"&gt;See it on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is no public library yet — only an idea, an implementation, and a set of tradeoffs.&lt;/p&gt;

&lt;p&gt;If this resonates with you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;as a user&lt;/li&gt;
&lt;li&gt;as a library author&lt;/li&gt;
&lt;li&gt;as someone who cares about language design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then feedback matters.&lt;/p&gt;

&lt;p&gt;Maybe you’ve already solved similar problems in a different way, and would like to share your approach.&lt;/p&gt;

&lt;p&gt;Should this remain a pattern?&lt;br&gt;
Should it become a small experimental library?&lt;br&gt;
Or should it stay an internal tool for specialized systems?&lt;/p&gt;

&lt;p&gt;I’m genuinely interested in hearing what the community thinks.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>programming</category>
      <category>software</category>
    </item>
  </channel>
</rss>
