Exceptions & Debugging
Transpose supports the full C# exception model, and the emitted JavaScript is readable enough to debug directly in the browser.
Exception handling
try, catch, finally, exception filters (when), and rethrow all work as they
do in .NET:
try
{
ThrowException();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("retry"))
{
Console.WriteLine("Filtered: " + ex.Message);
}
catch (InvalidOperationException ex)
{
Console.WriteLine("Caught specific exception: " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Caught general exception: " + ex.Message);
}
finally
{
Console.WriteLine("Finally block executed");
}
public void ThrowException()
{
throw new InvalidOperationException("Something went wrong!");
}
using / await using and IDisposable are supported, so deterministic cleanup
works too.
Exceptions and JavaScript errors
Transpose emits one JavaScript catch per try and dispatches to the matching C#
clause by type at runtime. A thrown value that is not already a System.Exception
is wrapped, so a native error thrown by library code is still catchable as a .NET
exception. The mapping is:
| Thrown JavaScript value | Caught as |
|---|---|
A System.Exception (from C# code) |
itself |
TypeError |
System.NullReferenceException |
RangeError |
System.ArgumentOutOfRangeException |
Any other Error |
System.SystemException |
| Anything else | System.Exception with the value's message |
The original error is kept on the wrapper, which is what StackTrace reads, so the
JavaScript stack survives into the .NET-shaped exception. InnerException,
Message and Data behave as expected.
In the other direction, an exception thrown from C# is a System.Exception
instance, not a JavaScript Error. JavaScript code that catches it should read
.message (which the runtime provides) rather than expect an Error prototype.
Debugging
Source maps are not emitted yet
Transpose does not currently produce .js.map files, so you cannot set
breakpoints in C# source in the browser. This is a known gap. Debug against the
emitted JavaScript instead — the notes below make that practical.
Build with formatted output
For a debugging session, make sure the readable bundle is what the page loads:
{
"outputFormatting": "Formatted"
}
A Debug build already wires the non-minified files into index.html, so plain
dotnet build is usually enough — see
Build Configurations.
Read the emitted JavaScript
The output keeps your names, which makes it far easier to navigate than typical compiler output:
- A type appears as
Transpose.define("MyNamespace.MyClass", { … }), so searching the bundle for the C# type name finds it. - Methods keep their C# names. Overloads are numbered with a
$1,$2… suffix (Foo,Foo$1), and constructors are$ctor1,$ctor2, …. - Auto-properties become plain fields; properties with bodies become
get_Name/set_Name. - Locals keep their C# names where JavaScript allows it.
Set breakpoints on those functions in the browser's Sources panel. Because the output is reproducible, the line you break on stays put across rebuilds unless the code above it changed.
Watch mode keeps the loop short
tps --watch rebuilds and reloads the page on every save, so the
edit-inspect-fix cycle does not involve a manual build. See
Watch mode.
Script.Debugger
Script.Debugger() emits a JavaScript debugger; statement, which breaks into the
devtools at that point when they are open:
using Transpose;
if (state == null)
{
Script.Debugger();
}
Common issues
| Symptom | Likely cause |
|---|---|
x is not a function on a value from a library |
The C# binding's [Name] or [Template] does not match the JavaScript API. Check the emitted call against the library's real surface. |
setItem is not a function after assigning an array to a Span<T> |
The implicit array-to-Span<T> conversion is not modelled. Use the array directly — see How It Works. |
| A struct assignment writes through to the original | Value-copy semantics apply only to structs declared in the project being compiled. BCL structs, library structs and ValueTuple copy by reference. |
| A reflection call finds nothing | reflection.disabled is on, or the type carries [Reflectable(false)]. See Reflection. |
| The whole bundle fails to parse | A syntax error in a Script.Write / [Script] / [Template] body. Raw JavaScript you supply is emitted verbatim and is not validated. |
| The page loads but nothing runs | tps.js is not loaded before the bundle, or the entry point is in a type the compiler did not see. Check the script order in index.html. |