Tsonic GitHub

Shape Phase

The Shape phase transforms the symbol graph for TypeScript emission. It runs multiple passes that analyze, synthesize, and modify the graph to handle CLR/TypeScript differences.

Pass Overview

Shape passes run in strict order. Each pass may depend on results from earlier passes.

Index Building (Setup)

# Pass Purpose
1 GlobalInterfaceIndex Build global index of all interfaces
2 InterfaceDeclIndex Build interface declaration index

Structural Analysis

# Pass Purpose
3 StructuralConformance Analyze which members satisfy interfaces
4 InterfaceInliner Flatten interface inheritance
5 InternalInterfaceFilter Remove BCL internal interfaces
6 ExplicitImplSynthesizer Synthesize explicit implementation members
7 EnumeratorConformancePass Promote Reset() for IEnumerator conformance

Hierarchy Resolution

# Pass Purpose
8 DiamondResolver Resolve diamond inheritance conflicts
9 BaseOverloadAdder Copy inherited overloads to derived types
10 StaticSideAnalyzer Analyze static member inheritance

Member Planning

# Pass Purpose
11 IndexerPlanner Plan indexer property handling
12 HiddenMemberPlanner Handle C# 'new' keyword hiding
13 FinalIndexersPass Final cleanup of indexer properties
14 ClassSurfaceDeduplicator Deduplicate class surface members

Constraint & Conflict Resolution

# Pass Purpose
15 ConstraintCloser Close generic constraint hierarchies
16 OverloadReturnConflictResolver Resolve overload return type conflicts

View Planning

# Pass Purpose
17 ViewPlanner Plan explicit interface views
18 MemberDeduplicator Final member deduplication

Plan Phase Passes (Post-Shape)

# Pass Purpose
19 OverloadUnifier Unify method overloads
20 InterfaceConstraintAuditor Audit generic constraints per interface
21 StaticHierarchyFlattener Plan static hierarchy flattening
22 StaticConflictDetector Detect static member conflicts
23 OverrideConflictDetector Detect override signature conflicts
24 PropertyOverrideUnifier Unify covariant property types
25 ExtensionMethodAnalyzer Analyze and bucket extension methods
26 SCCPlanner Plan SCC buckets for circular deps
27 InterfaceConformanceAnalyzer Analyze interface conformance
28 HonestEmissionPlanner Plan honest emission (safe implements)
29 SafeToExtendAnalyzer Determine safe interfaces to extend

Key Passes Explained

StructuralConformance

Analyzes which interface members a type structurally satisfies.

Problem: TypeScript uses structural typing. A class method add(item: T) might structurally match ICollection<T>.Add(T) even if names differ.

Solution: Track conformance relationships. Members that don't structurally match are marked ViewOnly.

class List<T> implements ICollection<T>
  - add(item: T) structurally matches ICollection.Add(T) -> ClassSurface
  - copyTo(array: T[]) doesn't match ICollection.CopyTo -> ViewOnly

InterfaceInliner

Flattens interface inheritance into a single member list.

Problem: Interface IList<T> extends ICollection<T> extends IEnumerable<T>. TypeScript needs all members visible.

Solution: Copy all inherited interface members to each interface's member list.

ExplicitImplSynthesizer

Creates members for C# explicit interface implementations.

C# Pattern:

class MyCollection : ICollection {
    void ICollection.CopyTo(Array array, int index) { }
}

TypeScript Output:

interface __MyCollection$views {
    As_ICollection(): ICollection;
}

DiamondResolver

Resolves diamond inheritance member conflicts.

Problem:

    IFoo
   /    \
IBar    IBaz
   \    /
  MyClass

Both IBar and IBaz may define conflicting members inherited from IFoo.

Solution: Pick one canonical declaration, mark others as ViewOnly.

BaseOverloadAdder

Copies inherited method overloads to derived types.

Problem: TypeScript requires all overloads at the same declaration site. C# spreads them across hierarchy.

Solution:

// All ToString overloads together
interface MyClass$instance {
    toString(): string;              // From Object
    toString(format: string): string; // From IFormattable
}

IndexerPlanner

Plans handling of C# indexer properties.

Problem: C# indexers with different parameter types create TypeScript overload conflicts.

Solution: Track indexers in metadata, emit only when safe.

ClassSurfaceDeduplicator

Deduplicates members on the class surface.

Problem: After inheritance flattening, multiple versions of same member may exist.

Solution: Pick winner based on specificity, mark others as duplicate.

ViewPlanner

Plans explicit interface views (the As_IInterface pattern).

Output:

interface __List_1$views<T> {
    As_IEnumerable_1(): IEnumerable_1<T>;
    As_ICollection(): ICollection;
}

Views provide access to interface members that don't structurally appear on the class surface.

PropertyOverrideUnifier

Handles covariant property return types.

Problem:

class Base { public virtual object Value { get; } }
class Derived : Base { public override string Value { get; } }

TypeScript doesn't support property overloading.

Solution: Emit union type:

interface Derived$instance extends Base$instance {
    readonly value: string | object;
}

ExtensionMethodAnalyzer

Buckets extension methods for C#-style "using" semantics.

Input: LINQ extension methods scattered across Enumerable, Queryable, etc.

Output: Emitted into __internal/extensions/index.d.ts as helper types:

// __internal/extensions/index.d.ts (excerpt)
export interface __Ext_System_Linq_IEnumerable_1<T> {
    where(predicate: System.Func_2<T, boolean>): ExtensionMethods_System_Linq<System_Collections_Generic.IEnumerable_1<T>>;
}

export type ExtensionMethods_System_Linq<TShape> =
    TShape & (TShape extends System_Collections_Generic.IEnumerable_1<infer T0> ? __Ext_System_Linq_IEnumerable_1<T0> : {});

SafeToExtendAnalyzer

Determines which interfaces can safely appear in extends clauses.

Problem: Some interface combinations cause TS2430 "Interface cannot simultaneously extend types".

Solution: Analyze structural compatibility. Safe interfaces go in extends, others become views.

EmitScope Assignment

Shape passes assign EmitScope to each member:

EmitScope Meaning
ClassSurface Emit on instance interface
StaticSurface Emit on static const
ViewOnly Emit only in view interface
Omitted Don't emit (tracked in metadata)

Transformation Flow

Raw SymbolGraph (from Load)
    │
    ▼ StructuralConformance
    │  └─ Members get SourceInterface, IsStructuralMatch
    ▼ InterfaceInliner
    │  └─ Interfaces flattened
    ▼ ExplicitImplSynthesizer
    │  └─ Explicit impl members synthesized
    ▼ DiamondResolver
    │  └─ Conflicts resolved, canonical members chosen
    ▼ BaseOverloadAdder
    │  └─ Inherited overloads copied
    ▼ ViewPlanner
    │  └─ ExplicitViews attached to types
    ▼
Shaped SymbolGraph (ready for naming)