This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
SharpTS is a TypeScript interpreter and compiler implemented in C# using .NET 10.0. It supports both tree-walking interpretation and ahead-of-time compilation to .NET IL.
dotnet build # Build the project
dotnet test # Run xUnit tests
dotnet test --filter "FullyQualifiedName~SomeTest" # Run specific test
dotnet run # Start REPL mode
dotnet run -- script.ts # Interpret a TypeScript file
dotnet run -- --compile script.ts # Compile to .NET assembly
dotnet run -- --compile script.ts -o out.dll # Custom output path
dotnet run -- --compile script.ts --verify # Verify emitted ILSource → Lexer → Parser → TypeChecker → Interpreter (tree-walk)
↘ ILCompiler (AOT to .NET IL)
| Directory | Purpose |
|---|---|
Parsing/ |
Lexer, Parser (14 partial files), AST node definitions |
TypeSystem/ |
Static type checking (47 files), type compatibility, generics |
Execution/ |
Tree-walking interpreter (16 files) |
Compilation/ |
IL compilation (316 files), async state machines, bundling |
Runtime/ |
Runtime values, environment, built-ins |
Runtime/Types/ |
TypeScript value types (87 files): arrays, classes, promises, etc. |
Runtime/BuiltIns/ |
Built-in method implementations (41 files) |
Runtime/Exceptions/ |
Control flow exceptions (return, break, continue, yield) |
Modules/ |
Module resolution, script detection |
Diagnostics/ |
Error reporting, source locations |
Packaging/ |
NuGet package generation |
Cli/ |
Command-line argument parsing |
Declaration/ |
TypeScript declaration generation from .NET types |
LspBridge/ |
Language Server Protocol bridge for IDE integration |
SharpTS.Tests/ |
xUnit test project |
Two-Environment System:
TypeEnvironment- Tracks types during static analysis (compile-time)RuntimeEnvironment- Tracks values during execution (runtime)- Never mix these - they serve completely different phases
Control Flow via Exceptions:
ReturnException,BreakException,ContinueException,YieldException- This is intentional - exceptions as control flow mechanism for unwinding
Type System:
- Structural typing for interfaces (duck typing)
- Nominal typing for classes (inheritance-based)
TypeInforecords represent types statically; runtime objects are independent
The codebase is migrating from object? to RuntimeValue struct to eliminate boxing:
RuntimeValue (Runtime/RuntimeValue.cs):
- 24-byte discriminated union storing primitives inline
ValueKindenum: Undefined, Null, Boolean, Number, String, Object, Symbol, BigInt- Factory methods:
FromNumber(),FromString(),FromBoolean(),FromObject(),FromBoxed() - Accessors:
AsNumber(),AsString(),AsBoolean(),AsObject<T>() - JavaScript semantics:
IsTruthy(),TypeofString()
ISharpTSCallableV2 (Runtime/Types/ISharpTSCallableV2.cs):
- New callable interface using
RuntimeValueinstead ofobject? CallableV2Adapterwraps legacy → V2,CallableLegacyAdapterwraps V2 → legacy- Extension methods:
.AsV2(),.AsLegacy(),.CallWithRuntimeValues()
BuiltInMethod (Runtime/BuiltIns/BuiltInMethod.cs):
- Implements both
ISharpTSCallableandISharpTSCallableV2 CreateV2()factory for RuntimeValue-based methods- Thread-local pooling in array built-ins to avoid allocations
All major phases use switch pattern matching on AST node types:
TypeChecker.Check()/CheckExpr()- static analysisInterpreter.Execute()/Evaluate()- runtimeILEmitter.EmitStatement()/EmitExpression()- IL compilation
All AST nodes are C# records in Parsing/AST.cs:
- Expression nodes inherit from
Expr - Statement nodes inherit from
Stmt - Use pattern matching to traverse
ILCompiler runs in multiple phases:
- Emit runtime types
- Analyze closures
- Define classes/functions
- Collect arrow functions
- Emit arrow bodies
- Emit class methods
- Emit entry point
- Finalize types
Arrow functions use display classes for captured variables; non-capturing arrows compile to static methods.
Compiled TypeScript DLLs must NOT reference SharpTS.dll. The output DLL must be fully standalone.
NEVER do this in Compilation/ files:
// BAD - embeds SharpTS.dll reference in output
var method = typeof(RuntimeTypes).GetMethod("SomeMethod");
il.Emit(OpCodes.Call, method);Instead, use reflection-based IL that resolves at runtime:
// GOOD - uses RuntimeEmitter helper methods
EmitReflectionCall(il, "SharpTS.Compilation.RuntimeTypes, SharpTS", "SomeMethod", argCount);
// or for void methods:
EmitReflectionCallVoid(il, "SharpTS.Compilation.RuntimeTypes, SharpTS", "SomeMethod", argCount);The same applies to PropertyDescriptorStore, ObjectBuiltIns, and any other SharpTS types.
Why: When emitting IL with typeof(X).GetMethod(...), the method token references the SharpTS assembly directly. This creates a hard dependency. The reflection pattern emits IL that does Type.GetType("..., SharpTS") at runtime, allowing graceful degradation if SharpTS isn't present.
- Type errors: "Type Error:" prefix
- Runtime errors: "Runtime Error:" prefix
- Compile errors: "Compile Error:" prefix
- For Loop Desugaring: Parser converts
forloops intowhileloops - console.log: Hardcoded special case in type checker, interpreter, and compiler
- Inner function declarations: Supported in IL compiler with hoisting, closure capture, and recursion
- Method Lookup: Searches up inheritance chain (see
TypeChecker.csCheckGet,Interpreter.Properties.csEvaluateGet)
STATUS.md- Feature implementation status and known bugsREADME.md- User documentation and examples