Skip to content

SupportedFeatures

Edgar Mesquita edited this page Jun 9, 2026 · 36 revisions

Supported C# Features Matrix

This document provides a comprehensive list of C# features, .NET APIs, and patterns supported by the eQuantic.UI compiler.

Legend

Full Support: Transpiles to equivalent JavaScript behavior.

⚠️ Partial Support: Works with caveats or minor differences.

🚧 Planned: On the roadmap.

Not Supported: fundamentally incompatible logic (e.g., blocking I/O, unsafe pointers).


🏗️ Core Language Features

Feature Status Notes
Classes & Structs Transpiled to ES6 Classes.
Interfaces Used for TypeScript type checking (erased at runtime).
Enums Transpiled to JS Objects (bidirectional mapping).
Generics Fully supported (erased at runtime).
Extension Methods Resolved via Semantic Model and transpiled to direct calls.
Async / Await Maps to async / await and Promise.
Lambda Expressions Maps to Arrow Functions () => {}.
Pattern Matching is patterns, Property Patterns, Recursive Patterns.
String Interpolation Maps to Template Literals `${var}`.
Null-Coalescing ?? and ??= mapped to JS equivalents.
Object Initializers new Obj { Prop = 1 }.
Collection Initializers new List<int> { 1, 2 } maps to [1, 2].
Deconstruction var (a, b) = tuple maps to [a, b] = tuple.
Local Functions Transpiled to inner functions.
Records (with) Maps to object spread { ...src, prop: val }.
typeof Maps to type name string literal.
base calls Maps to super keyword.
cast & as Maps to JS passthrough/truncation.
sizeof Maps to C# primitive sizes.
Anonymous Methods delegate(...) { ... } maps to arrow functions.
stackalloc Maps to Typed Arrays (e.g., Int32Array).
yield return Maps to JS Generator Functions (function*).
lock ⚠️ Transpiled to a no-op block (JS is single-threaded).

🔄 LINQ Support

The compiler includes specialized strategies for nearly all LINQ methods.

Logic Methods Status
Filtering Where, OfType
Projection Select, SelectMany, Cast
Partitioning Skip, Take, SkipWhile, TakeWhile
Ordering OrderBy, OrderByDescending, ThenBy, Reverse
Aggregation Count, Sum, Min, Max, Average, Aggregate
Quantifiers Any, All, Contains
Sets Distinct, DistinctBy, Union, Intersect, Except, Concat
Elements First, FirstOrDefault, Single, Last, ElementAt
Utility SequenceEqual, DefaultIfEmpty
Conversion ToList, ToArray, ToDictionary, ToHashSet
Grouping/Join GroupBy, Join, Zip

📦 .NET Types (BCL)

We map common .NET types to their JavaScript equivalents.

Primitives

.NET Type JavaScript Equivalent
string String
int, double, float Number
bool Boolean
object Object
dynamic any

System.String

  • Join, Format
  • IsNullOrEmpty, IsNullOrWhiteSpace
  • Split, Replace, Substring, Trim
  • ToLower, ToUpper, StartsWith, EndsWith

System.DateTime, TimeSpan, DateOnly, TimeOnly & DateTimeOffset

The temporal types are backed by tick-precise compat types (100-ns ticks, proleptic Gregorian calendar — not the lossy new Date() / numeric-milliseconds mapping). Ctors, components, Add*, arithmetic (-TimeSpan), comparisons, and invariant .ToString() all match .NET; values cross the SSR wire as ISO-8601 / "c" strings and are hydrated back into the compat type. See the .NET BCL Coverage & Conformance table below for the per-type detail.

System.Collections.Generic

  • List<T> → Javascript Array []
  • Dictionary<TKey, TValue> → Javascript Object {} or Map (depending on usage).
  • HashSet<T> → Javascript Set.
  • Queue<T>, Stack<T> → Javascript Array methods (push/shift/pop).

System.Threading.Tasks

  • Task, Task<T>Promise.
  • Task.DelaysetTimeout wrapper.
  • Task.WhenAll, Task.WhenAny.
  • ⚠️ Task.Run executes on the main thread (microtask), NOT a background thread.

Other Utilities

  • Console.WriteLineconsole.log.
  • Math.* (Min, Max, Abs, Round, etc.) → Math.*.
  • Guid (NewGuid, Empty, Parse) → crypto.randomUUID().
  • Regex → JavaScript RegExp.

🌐 Ecosystem Packages

eQuantic.UI.Lucide / Heroicons / RadixIcons / TablerIcons / Phosphor / SimpleIcons / BootstrapIcons / Iconoir / ...

Purpose: Comprehensive Icon Sets. Contains:

  • SVG resolution logic
  • Specialized icon components
  • IIconProvider implementation
Package Purpose Status
eQuantic.UI.Charts (Apex/ChartJS) High-performance visualization.
eQuantic.UI.Lottie High-performance animations.
eQuantic.UI.Image Optimized (Lazy/Blur/Priority).
eQuantic.UI.Tailwind Standard styling integration.

🧩 Framework Patterns

Pattern Interface Description Status
Metadata Management IHandleMetadata SEO tags and head elements.
Asset Management IRequireAssets Dynamic script/style injection.
Server Actions [ServerAction] Secure RPC from Client to Server.
Compound Components N/A Semantic sub-component patterns.

⚠️ Limitations & Caveats

  1. Blocking Code:

    • .Wait(), .Result on Tasks are NOT supported. You must use await. Blocking the main thread freezes the browser UI.
  2. Reflection:

    • System.Reflection is largely unsupported.
    • typeof(T).Name and nameof(...) are supported constants.
  3. File System:

    • System.IO (File, Directory) is not strictly forbidden but will fail at runtime in the browser.
    • Use Server Actions to handle file operations.
  4. Numbers:

    • int/double/float map to JS numbers. Integer division truncates (Math.trunc) and Math.Round uses banker's rounding (MidpointRounding.ToEven) via the round compat helper.
    • decimal is exact end-to-end — compiled to the runtime Decimal compat type (BigInt mantissa + scale), so 0.1m + 0.2m == 0.3m is true. Decimals also cross the wire as JSON strings (EqJson) and are hydrated back into Decimal on the client (the field's Decimal default drives a type-preserving coercion — see hydrate-value.ts), so server-provided decimals keep all 28 digits instead of rounding through a double.
    • long/ulong are now exact — compiled to JS BigInt via the long compat helper. 9007199254740993L + 1L is 9007199254740994 (a plain JS number would round to …992). Literals become BigInt (5L5n); arithmetic/comparison operands are wrapped in long() (which coerces number/stringbigint) so mixed expressions never throw. On the wire, 64-bit ints cross as JSON strings (Server Actions + SSR state, via EqJson) so values beyond 2^53 survive the round trip. Other numeric type suffixes (1.5f, 100u) are stripped.
  5. Thread Safety:

    • Since JS is single-threaded, lock statements are compiled away (ignored).
    • Thread.Sleep is not supported (use Task.Delay).

🧪 .NET BCL Coverage & Conformance

Transpilation fidelity is enforced by a conformance harness (tests/eQuantic.UI.Conformance.Tests): each case runs the same C# expression two ways — transpiled to JS (executed via the embedded Bun) and evaluated directly in .NET (Roslyn scripting) — and asserts identical results. 420+ cases are green.

(Most recent: records emit as named classes carrying instance methods.) This covers both expressions and statement blocks (control flow: if/for/foreach/while/switch/ try-catch-finally/local functions — the block runs in an IIFE and its returned value is compared).

Every construct resolves via one of three mechanisms (see docs/DOTNET-COVERAGE-PROGRAM.md):

  1. Native strategy — idiomatic JS when the runtime has an equivalent.

  2. .NET-compat runtime helper — faithful .NET semantics where JS has none. The transpiler emits these under a single namespace $eq (organised by domain), brought in with one import per moduleimport { $eq } from "@equantic/runtime" (resolved by the page's import map) — instead of N loose helper imports, and $eq.* can never collide with a user identifier in the generated scope:

    • $eq.numdec (exact Decimal), long (Int64 via BigInt)
    • $eq.mathround (banker's rounding)
    • $eq.textformat (number/string formatting), stringBuilder
    • $eq.timedateTime, timeSpan, dateOnly, timeOnly, dateTimeOffset
    • $eq.enumsparse (enum member-name)
    • $eq.collectionsqueue (FIFO), stack (LIFO)
    • $eq.nullablearith, cmp (lifted Nullable<T> operators: null-propagating arithmetic, false-on-null relational)
    • $eq.equals — structural (value) equality for records/structs/tuples (backs ==, .Equals, Contains, Distinct)
    • $eq.cssstyleBuilder, classBuilder, joinClasses, whenClass (the styling subsystem)
  3. Fail-on-unsupported — a construct with no possible JS representation now raises a build error (with a stable EQ code) instead of being silently emitted verbatim. Two layers:

    • UnsupportedConstructStrategy (EQ2001) — typed-reference intrinsics (__makeref, __refvalue, __reftype), pointer types, function pointers.
    • goto/goto case/goto default (EQ2002) — no JS equivalent; restructure with loops/conditionals. (unsafe/fixed/lock blocks unwrap to their body — lock is a single-threaded no-op — and a bare label drops to its inner statement.)
    • SemanticValidator client/server boundary (EQ21xx) — calls into System.IO, System.Net.Http, System.Net.Sockets, EF Core / System.Data, OS threading (Thread/Monitor/Mutex), Process, InteropServices (P/Invoke), Reflection.Emit from a client component. (System.Threading.Tasks is not forbidden — async maps to Promise.) The fix is to move the call into a [ServerAction].

    Any other construct that hits no strategy is emitted verbatim but now reported as a warning (EQ1001/EQ1002) so it is visible rather than silent. Diagnostics print in MSBuild-canonical form, so errors fail dotnet build.

Area Status Notes
Arithmetic / bitwise / comparison integer division truncates; %, shifts, `&
Math.* Truncatetrunc, Ceilingceil, Round banker's via round helper
decimal exact via Decimal (literals + + - * / == != < > <= >=)
Numeric constants int.MaxValue, double.Epsilon, … → literals
Parsing / Convert.* int/double.Parse, bool.Parse, Convert.ToInt32/ToDouble/ToString/ToBoolean/…
Strings Substring/IndexOf/Replace/Split/Pad/Trim(char)/Concat/Format/Join/IsNullOrEmpty/IsNullOrWhiteSpace; StringComparison-aware Equals/StartsWith/EndsWith/Contains/IndexOf (Ordinal + IgnoreCase). Culture-sensitive ordering (CompareTo) is out of scope.
char.* ToUpper/ToLower/IsDigit/IsLetter/IsWhiteSpace/… (Unicode-aware)
StringBuilder compat type — Append(incl. bool→"True"/"False")/AppendLine("\n")/Insert/Remove/Replace/Clear/Length/ToString
LINQ Where/Select/SelectMany/Where-Select(indexed)/OrderBy/Distinct(By)/GroupBy/ToDictionary/ToLookup/Zip/Chunk/MinBy/MaxBy/Take(While)/Skip(While)/Aggregate/Sum/Min/Max/Average/Count/Any/All/First/Last/Concat/Reverse/Join/GroupJoin/ThenBy/ThenByDescending (Join/GroupJoin = order-preserving hash join over primitive keys; OrderBy+ThenBy = single stable composite sort, source copied). IGrouping from GroupBy/ToLookup is usable as a sequence (iterate, g.Select/g.Sum/g.Count()) and exposes g.Key; ILookup [key] indexer is not modelled.
Collections List, Dictionary, HashSet (incl. initializers and .Count); Queue/Stack compat (Enqueue/Dequeue/Push/Pop/Peek/Count/Contains/ToArray)
enum member-name string (equality/switch/ternary)
long/ulong exact via BigInt (long helper); literals 5L5n, wire as JSON string
DateTime tick-precise DateTime compat type — ctors, components, Add*, -TimeSpan, comparisons, .ToString()/format; ISO-8601 wire + hydration
TimeSpan tick-precise TimeSpan compat type — From*, ctors, components/totals, + -, comparisons, .NET "c" .ToString(); "c" wire + hydration
DateOnly / TimeOnly compat types (.NET 6+) — ctors, components, Add*(+ TimeOnly wrap), comparisons, invariant .ToString() (MM/dd/yyyy / HH:mm); ISO wire + hydration
DateTimeOffset tick-precise compat type (wall-clock + offset, compared by the instant) — ctors, components, Offset/UtcDateTime/LocalDateTime, ToOffset, Add*, From/ToUnixTime*, - TimeSpan, instant comparisons, invariant .ToString() (MM/dd/yyyy HH:mm:ss zzz); ISO+offset wire + hydration
record / struct / value tuple Value semantics. Records/structs are plain objects (positional new Point(1,2){x,y}, object initializers merge), tuples are arrays with element access by position (t.Item1) and by declared name ((int X, int Y).X) → index. ==/!=, .Equals, Contains, Distinct compare structurally via $eq.equals; with copies-and-replaces. Deconstruction var (a, b) = … works for tuples (array destructuring, discard holes) and records (object destructuring by Deconstruct order). Records emit as named JS classes carrying their user instance methods + a structural equals, prototype-preserving with, and .NET toString; value semantics are unchanged. Remaining: real build-pipeline emission/import wiring, SSR re-hydration of record instances, record-keyed dictionaries.
Nullable<T> (T?) HasValue/Value, GetValueOrDefault() (type-aware default: 0/false/$eq.num.dec(0)/enum zero-member/…) and GetValueOrDefault(fallback), ??; lifted operators via $eq.nullable.* — arithmetic propagates null, relational (< > <= >=) is false when either side is null (not a numeric coercion). No-arg GetValueOrDefault() on DateTime?/Guid?/struct yields null — use the fallback form there.
Guid Guid.NewGuid()crypto.randomUUID(), Guid.Empty, Guid.Parse; string wire

Clone this wiki locally