TypeScript vs Java vs C# vs Go

Type System, Concurrency, Memory & Language Features — Complete Side-by-Side Technical Reference

1. Type System Model

TypeScript Structural Typing

Compatibility based on shape, not name.

Definition
type User = { name: string };
type Employee = { name: string; role: string };
✅ Works
const emp: Employee = { name: "Ana", role: "dev" };
const user: User = emp; // ✅ structural match

function greet(u: User) { console.log(u.name); }
greet(emp);            // ✅ Employee satisfies User shape
greet({ name: "Ana" }); // ✅ inline object matches
❌ Does Not Work
const partial = { role: "dev" };
const u: User = partial;
// ❌ Property 'name' is missing

greet({ name: "Ana", age: 30 });
// ❌ Object literal may only specify known properties

Java Nominal Typing

Compatibility requires explicit declared relationship.

Definition
class User { String name; }
class Employee { String name; String role; }
✅ Works
User user = new User();
user.name = "Ana"; // ✅

// With explicit relationship
class Employee extends User { String role; }
User u = new Employee(); // ✅ subclass
❌ Does Not Work
class A { String name; }
class B { String name; }
A a = new A();
B b = a;
// ❌ incompatible types — no relationship declared

C# Nominal Typing

Nominal like Java, but with implicit interface implementation via duck typing in some contexts.

Definition
class User { public string Name { get; set; } }
class Employee { public string Name { get; set; } public string Role { get; set; } }
✅ Works
// With explicit relationship
class Employee : User { public string Role { get; set; } }
User u = new Employee(); // ✅

// foreach works on any type with GetEnumerator() — duck typing
class MyList {
  public IEnumerator<int> GetEnumerator() => /* ... */;
}
foreach (var x in new MyList()) { } // ✅ no interface needed
❌ Does Not Work
class A { public string Name { get; set; } }
class B { public string Name { get; set; } }
A a = new A();
B b = a;
// ❌ Cannot implicitly convert type 'A' to 'B'

Go Structural (Interfaces)

Interfaces are structural — no "implements" keyword. Concrete types are nominal.

Definition
type User struct { Name string }
type Namer interface { GetName() string }

func (u User) GetName() string { return u.Name }
✅ Works
// Implicit interface satisfaction
var n Namer = User{Name: "Ana"} // ✅ no "implements"
fmt.Println(n.GetName()) // ✅

// Any type with GetName() satisfies Namer
type Bot struct{}
func (b Bot) GetName() string { return "Bot" }
var n2 Namer = Bot{} // ✅
❌ Does Not Work
type A struct { Name string }
type B struct { Name string }
var a A = A{Name: "x"}
var b B = a
// ❌ cannot use a (type A) as type B

// Missing method → interface not satisfied
type Broken struct{}
var n3 Namer = Broken{}
// ❌ Broken does not implement Namer (missing GetName)
2. Union / Variant Modeling

TypeScript Discriminated Unions

Tagged unions narrowed via literal discriminant field.

Definition
type Success = { status: "success"; data: string };
type Failure = { status: "error"; message: string };
type ApiResponse = Success | Failure;
✅ Works
function handle(r: ApiResponse) {
  if (r.status === "success") {
    console.log(r.data);   // ✅ narrowed to Success
  } else {
    console.log(r.message); // ✅ narrowed to Failure
  }
}
handle({ status: "success", data: "OK" }); // ✅
❌ Does Not Work
function broken(r: ApiResponse) {
  console.log(r.data);
  // ❌ 'data' does not exist on type 'Failure'
}
handle({ status: "maybe", data: "x" });
// ❌ '"maybe"' not assignable to '"success"|"error"'

Java Sealed Types (17+)

Sealed class/interface hierarchy with exhaustive pattern matching.

Definition
sealed interface ApiResponse
  permits Success, Failure {}
final class Success implements ApiResponse { String data; }
final class Failure implements ApiResponse { String message; }
✅ Works
String result = switch (r) {
  case Success s -> s.data;    // ✅
  case Failure f -> f.message; // ✅
}; // exhaustive check by compiler
❌ Does Not Work
final class Timeout implements ApiResponse {}
// ❌ not allowed in sealed hierarchy

System.out.println(r.data);
// ❌ cannot find symbol — must narrow first

C# Abstract Records + Pattern Matching

Abstract records with recursive pattern matching and switch expressions.

Definition
abstract record ApiResponse;
record Success(string Data) : ApiResponse;
record Failure(string Message) : ApiResponse;
✅ Works
string Handle(ApiResponse r) => r switch {
  Success s => s.Data,      // ✅
  Failure f => f.Message,   // ✅
  _ => throw new Exception()
};

// Recursive patterns
if (r is Success { Data: "OK" }) { } // ✅ nested match
❌ Does Not Work
Console.WriteLine(r.Data);
// ❌ 'ApiResponse' does not contain 'Data'

// Not sealed by default — anyone can subclass
// Use 'file' access modifier or internal to limit

Go Interface + Type Switch

Interfaces with marker methods and runtime type switches.

Definition
type ApiResponse interface{ isResponse() }
type Success struct{ Data string }
type Failure struct{ Message string }
func (Success) isResponse() {}
func (Failure) isResponse() {}
✅ Works
func handle(r ApiResponse) string {
  switch v := r.(type) {
  case Success: return v.Data    // ✅
  case Failure: return v.Message // ✅
  default: return ""
  }
}
❌ Does Not Work
fmt.Println(r.Data)
// ❌ r.Data undefined (type ApiResponse has no field Data)

// No exhaustiveness check — compiler won't warn
// if you add a new variant and forget to handle it
3. Template Literal Types

TypeScript Compile-time String Composition

Compose string literal types at the type level using template syntax.

Definition
type Entity = "rates" | "services";
type Path = `/${Entity}`;
type Event = `user:${"created" | "deleted"}`;
✅ Works
const p: Path = "/rates";        // ✅
const e: Event = "user:created"; // ✅
function navigate(path: Path) { }
navigate("/services"); // ✅
❌ Does Not Work
const bad: Path = "/users";
// ❌ '"/users"' not assignable to '"/rates"|"/services"'
navigate("/rates/123");
// ❌ not assignable to type Path

Java No Equivalent

No compile-time string type composition.

✅ Works (runtime only)
Set<String> VALID = Set.of("/rates", "/services");
void navigate(String path) {
  if (!VALID.contains(path))
    throw new IllegalArgumentException(path);
}
navigate("/rates"); // ✅ at runtime
❌ Does Not Work
navigate("/users"); // compiles ✅ → ❌ runtime exception
// No way to catch at compile time

C# No Equivalent

No compile-time string literal types.

✅ Works (runtime only)
void Navigate(string path) {
  if (path is not "/rates" and not "/services")
    throw new ArgumentException(path);
}
Navigate("/rates"); // ✅ at runtime
❌ Does Not Work
Navigate("/users"); // compiles ✅ → ❌ runtime exception
// Same limitation as Java — String is String

Go No Equivalent

No compile-time string literal types.

✅ Works (runtime only)
func navigate(path string) error {
  switch path {
  case "/rates", "/services": return nil
  default: return fmt.Errorf("invalid: %s", path)
  }
}
navigate("/rates") // ✅
❌ Does Not Work
navigate("/users") // compiles ✅ → ❌ returns error
// Go has no string literal types
4. Mapped Types

TypeScript Programmatic Type Transformation

Transform types programmatically using keyof, in, and conditional types.

Definition
type User = { id: string; name: string; email: string };
type ReadonlyUser = { readonly [K in keyof User]: User[K] };
type PartialUser  = { [K in keyof User]?: User[K] };
✅ Works
function update(id: string, patch: PartialUser) { /* ... */ }
update("1", { name: "Ana" }); // ✅ only name
update("1", {});                  // ✅ empty

const frozen: ReadonlyUser = { id:"1", name:"A", email:"a@b" };
console.log(frozen.name); // ✅
❌ Does Not Work
frozen.name = "B";
// ❌ Cannot assign to 'name' — read-only property
update("1", { name: "X", age: 30 });
// ❌ 'age' does not exist in type PartialUser

Java Manual Duplication

No type-level transformations.

✅ Works
record User(String id, String name, String email) {}
// Must create a separate DTO class manually
record UserPatch(String name, String email) {} // "partial"
var p = new UserPatch("Ana", null); // ✅
❌ Does Not Work
// Cannot derive Partial<User> automatically
// No compile-time meta-programming at type level

C# Source Generators (partial)

No mapped types natively, but source generators can auto-generate variants.

✅ Works
record User(string Id, string Name, string Email);

// "with" expressions create modified copies
var user = new User("1", "Ana", "a@b.com");
var updated = user with { Name = "Ana" }; // ✅ immutable update
❌ Does Not Work
// Cannot derive Partial<User> at type level
// Must write separate DTO or use nullable props manually
user.Name = "X";
// ❌ init-only property — cannot assign after init

Go Manual Structs

No type-level transformation. Use pointer fields for optional.

Definition
type User struct { ID, Name, Email string }
✅ Works
type User struct { ID, Name, Email string }

// "Partial" via pointer fields
type UserPatch struct {
  Name  *string
  Email *string
}
name := "Ana"
patch := UserPatch{Name: &name} // ✅ only name set
❌ Does Not Work
// No Readonly wrapper — must use unexported fields
// No way to derive types from other types programmatically
5. Utility Types

TypeScript Built-in Type Helpers

Standard library of type transformations: Pick, Omit, Exclude, Extract, etc.

Definition
type User = { id: string; name: string; email: string };
type Preview  = Pick<User, "id" | "name">;
type Create   = Omit<User, "id">;
type Entity   = "rates" | "services" | "all";
type Single   = Exclude<Entity, "all">;
✅ Works
const p: Preview = { id: "1", name: "Ana" }; // ✅
const c: Create = { name: "Ana", email: "a@b" }; // ✅
function fetchOne(e: Single) {}
fetchOne("rates"); // ✅
❌ Does Not Work
const bad: Preview = { id:"1", name:"X", email:"e" };
// ❌ 'email' does not exist in Pick<...>
fetchOne("all");
// ❌ '"all"' not assignable to '"rates"|"services"'

Java No Equivalent

Must define each projection as a separate record/class.

record User(String id, String name, String email) {}
record UserPreview(String id, String name) {} // manual Pick
record CreateUser(String name, String email) {} // manual Omit

C# No Equivalent

No type-level Pick/Omit. Separate DTOs or anonymous types.

✅ Works
// Anonymous types for ad-hoc projections
var preview = new { user.Id, user.Name }; // ✅
Console.WriteLine(preview.Name); // ✅

// LINQ projection
var previews = users.Select(u => new { u.Id, u.Name }); // ✅
❌ Does Not Work
// Anonymous types can't be passed across method boundaries easily
void Show(??? preview) { } // ❌ no way to declare the type

Go No Equivalent

Must define separate structs. No generics-based type transformation.

type User struct { ID, Name, Email string }
type UserPreview struct { ID, Name string }  // manual
type CreateUser struct { Name, Email string } // manual
6. Generics

TypeScript Full Generics

Structural generics with full type inference and constraint support.

Definition
function identity<T>(value: T): T { return value; }
interface Repo<T> { find(id: string): T | undefined; }
✅ Works
const n = identity(42);    // ✅ inferred as number
function len<T extends { length: number }>(x: T) { return x.length; }
len("abc"); // ✅
len([1,2]); // ✅
❌ Does Not Work
len(42); // ❌ number has no 'length'
function bad<T>(v: T): T { return v.toString(); }
// ❌ Type 'string' not assignable to 'T'

Java Generics (Type Erasure)

Generics erased at runtime. Bounded type parameters for constraints.

Definition
public <T> T identity(T value) { return value; }
✅ Works
String s = identity("hello"); // ✅
<T extends Comparable<T>> T max(T a, T b) {
  return a.compareTo(b) >= 0 ? a : b;
}
max(3, 5); // ✅ → 5
❌ Does Not Work
if (list instanceof List<String>) {} // ❌ erasure
T[] arr = new T[10];                  // ❌ generic array
List<int> nums;                       // ❌ must use Integer

C# Reified Generics

Generic type info preserved at runtime — no erasure.

Definition
T Identity<T>(T value) => value;
✅ Works
var n = Identity(42);  // ✅ inferred as int
var s = Identity("x"); // ✅

// Runtime type available — no erasure!
Type t = typeof(List<string>);
Console.WriteLine(t.GenericTypeArguments[0]); // ✅ → String

// Primitives work directly
List<int> nums = new() { 1, 2, 3 }; // ✅ no boxing
❌ Does Not Work
T Create<T>() => new T();
// ❌ Cannot create instance of T — need 'where T : new()' constraint

void Bad<T>(T val) { int x = (int)val; }
// ❌ Cannot convert type 'T' to 'int'

Go Generics (1.18+)

Type parameters with interface constraints. Monomorphized at compile time.

Definition
func Identity[T any](value T) T { return value }
✅ Works
n := Identity(42)    // ✅ inferred
s := Identity("hi")  // ✅

// Constraints
func Max[T constraints.Ordered](a, b T) T {
  if a > b { return a }
  return b
}
Max(3, 5) // ✅ → 5
❌ Does Not Work
func Bad[T any](v T) { fmt.Println(v.Name) }
// ❌ v.Name undefined — 'any' has no fields

// No generic methods on types (only functions and types)
// No variance/wildcards
7. Variance

TypeScript Structural / Implicit

Variance inferred from structural assignability.

Definition
type Animal = { name: string };
type Dog = Animal & { breed: string };
type Logger<T> = (item: T) => void;
✅ Works
type Animal = { name: string };
type Dog = Animal & { breed: string };
let dogs: Dog[] = [{ name: "Rex", breed: "Lab" }];
let animals: Animal[] = dogs; // ✅ structural covariance

type Logger<T> = (item: T) => void;
const logAnimal: Logger<Animal> = (a) => console.log(a.name);
const logDog: Logger<Dog> = logAnimal; // ✅ contravariant param
❌ Does Not Work
const logDogOnly: Logger<Dog> = (d) => console.log(d.breed);
const logAny: Logger<Animal> = logDogOnly;
// ❌ strictFunctionTypes — Dog fn can't handle all Animals

Java Explicit Wildcards

Use-site variance with ? extends (covariant) and ? super (contravariant).

Definition
class Animal { String name; }
class Dog extends Animal { String breed; }
// ? extends T = covariant read
// ? super T = contravariant write
✅ Works
List<Dog> dogs = List.of(new Dog());
List<? extends Animal> animals = dogs; // ✅ covariant read
Animal a = animals.get(0); // ✅

List<? super Dog> sink = new ArrayList<Animal>();
sink.add(new Dog()); // ✅ contravariant write
❌ Does Not Work
List<Animal> a2 = dogs; // ❌ invariant by default
animals.add(new Dog()); // ❌ can't write to covariant
Dog d = sink.get(0);    // ❌ returns Object from contravariant

C# Declaration-site (in/out)

Variance declared on the interface, not at use-site.

Definition
// IEnumerable<out T> → covariant (producer)
// Action<in T> → contravariant (consumer)
// List<T> → invariant (both in and out)
✅ Works
// IEnumerable<out T> — covariant
IEnumerable<Dog> dogs = new List<Dog>();
IEnumerable<Animal> animals = dogs; // ✅

// Action<in T> — contravariant
Action<Animal> logAnimal = a => Console.WriteLine(a.Name);
Action<Dog> logDog = logAnimal; // ✅
❌ Does Not Work
List<Animal> a = new List<Dog>();
// ❌ List<T> is invariant (not IEnumerable<out T>)

// Cannot use 'out' on mutable interfaces
interface IBad<out T> { void Add(T item); }
// ❌ 'T' is covariant but used as input

Go No Variance

Go has no covariance or contravariance for generics.

Definition
type Animal interface { GetName() string }
type Dog struct { Name, Breed string }
func (d Dog) GetName() string { return d.Name }
✅ Works
// Interfaces provide implicit "variance" via satisfaction
type Animal interface { GetName() string }
type Dog struct { Name, Breed string }
func (d Dog) GetName() string { return d.Name }

var a Animal = Dog{Name: "Rex"} // ✅
❌ Does Not Work
var dogs []Dog = []Dog{{}}
var animals []Animal = dogs
// ❌ cannot use []Dog as []Animal
// Must convert element by element
8. Function Types & Overloads

TypeScript First-class Functions

Functions are values with typed signatures. Overloads via declaration merging.

Definition
type Handler = (v: string) => void;
function greet(name: string): string;
function greet(age: number): string;
✅ Works
type Handler = (v: string) => void;
const log: Handler = v => console.log(v); // ✅
log("hi"); // ✅

// Overloads
function greet(name: string): string;
function greet(age: number): string;
function greet(v: string | number) { return String(v); }
greet("Ana"); // ✅
greet(30);       // ✅
❌ Does Not Work
log(42); // ❌ number not assignable to string
greet(true); // ❌ no overload matches

Java Functional Interfaces + True Overloads

SAM interfaces for lambdas. True method overloading at compile time.

Definition
Consumer<String> log = System.out::println;
Function<String, String> toUpper = String::toUpperCase;
✅ Works
Consumer<String> log = v -> System.out.println(v); // ✅
log.accept("hi"); // ✅

Function<String,String> toUpper = String::toUpperCase; // ✅

// True overloads — separate implementations
String greet(String name) { return "Hi " + name; }
String greet(int age) { return "Age: " + age; }
❌ Does Not Work
log.accept(42); // ❌ int → String
var fn = (String s) -> s.length(); // ❌ can't infer type
int greet(String n) { return n.length(); }
// ❌ already defined — can't overload by return type only

C# Delegates + True Overloads

Delegate types and lambda expressions. True method overloading plus local functions.

Definition
Action<string> log = Console.WriteLine;
Func<string, string> toUpper = s => s.ToUpper();
✅ Works
Action<string> log = v => Console.WriteLine(v); // ✅
log("hi"); // ✅

Func<string, string> toUpper = s => s.ToUpper(); // ✅

// True overloads
string Greet(string name) => $"Hi {name}";
string Greet(int age) => $"Age: {age}";
Greet("Ana"); // ✅
Greet(30);       // ✅

// Local functions
void Process() {
  int Square(int x) => x * x; // ✅ local function
}
❌ Does Not Work
log(42); // ❌ cannot convert int to string
int Greet(string name) => name.Length;
// ❌ already defines Greet(string)

Go First-class Functions, No Overloads

Functions are first-class values. No overloading — each name must be unique.

Definition
type Handler func(string)
var log Handler = func(v string) { fmt.Println(v) }
✅ Works
type Handler func(string)
var log Handler = func(v string) { fmt.Println(v) } // ✅
log("hi") // ✅

// Functions as values
apply := func(fn func(int) int, v int) int { return fn(v) }
apply(func(x int) int { return x * 2 }, 5) // ✅ → 10
❌ Does Not Work
// No function overloading at all
func greet(name string) string { return name }
func greet(age int) string { return "" }
// ❌ greet redeclared in this block

log(42) // ❌ cannot use int as string
9. Null Safety

TypeScript Union Null Types

Null/undefined tracked in the type system via union types and strictNullChecks.

Definition
let name: string | null = null;
let age: number | undefined;
✅ Works
let name: string | null = null;
if (name !== null) {
  console.log(name.toUpperCase()); // ✅ narrowed
}
const city = user?.address?.city; // ✅ optional chain
const display = name ?? "Anon";   // ✅ nullish coalescing
❌ Does Not Work
console.log(name.toUpperCase());
// ❌ 'name' is possibly 'null'
function len(s: string) { return s.length; }
len(name); // ❌ string|null not assignable to string

Java Optional (no compile enforcement)

Optional wrapper class. Compiler does not prevent null dereferences.

Definition
Optional<String> opt = Optional.ofNullable(value);
String name = null; // allowed — no compile error
✅ Works
Optional<String> opt = Optional.ofNullable(name);
opt.ifPresent(n -> System.out.println(n.toUpperCase())); // ✅
String display = opt.orElse("Anon"); // ✅

if (name instanceof String s) {
  System.out.println(s.toUpperCase()); // ✅ null-safe
}
❌ Does Not Work
String name = null;
name.toUpperCase(); // ❌ NullPointerException at runtime
Optional.of(null);  // ❌ NPE — use .ofNullable()
// Compiler does NOT prevent null dereference

C# Nullable Reference Types (8.0+)

Compile-time warnings for null misuse when enabled.

Definition
#nullable enable
string? name = null;   // explicitly nullable
string safe = "hello"; // non-nullable by default
✅ Works
#nullable enable
string? name = null; // explicitly nullable
if (name is not null) {
  Console.WriteLine(name.ToUpper()); // ✅ narrowed
}
string display = name ?? "Anon"; // ✅
int? age = null;
int safe = age ?? 0; // ✅ value type nullable
❌ Does Not Work
string? name = null;
Console.WriteLine(name.Length);
// ⚠️ CS8602: Dereference of a possibly null reference

string nonNull = name;
// ⚠️ CS8600: Converting null literal to non-nullable

Go Zero Values + nil

No null — uses zero values. Pointers/interfaces can be nil.

Definition
var s string    // zero value: ""
var n int       // zero value: 0
var p *string   // zero value: nil
✅ Works
var name string // zero value: "" (not nil)
fmt.Println(len(name)) // ✅ → 0, no crash

// Pointers for optional values
var p *string = nil
if p != nil {
  fmt.Println(*p) // ✅ safe dereference
}
❌ Does Not Work
var p *string = nil
fmt.Println(*p) // ❌ runtime panic: nil pointer dereference

var m map[string]int // nil map
m["key"] = 1 // ❌ runtime panic: assignment to nil map
// Compiler does NOT catch nil dereferences
10. Enums & Constants

TypeScript Union Types / const

String literal unions preferred over enum keyword. Fully type-safe.

Definition
type Status = "OPEN" | "CLOSED";
const STATUS = { OPEN: "OPEN", CLOSED: "CLOSED" } as const;
✅ Works
type Status = "OPEN" | "CLOSED";
function close(t: { status: Status }) {
  if (t.status === "OPEN") console.log("Closing"); // ✅
}
close({ status: "OPEN" }); // ✅
❌ Does Not Work
close({ status: "PENDING" });
// ❌ '"PENDING"' not assignable to '"OPEN"|"CLOSED"'

Java Full Enum Classes

Enums are full classes with methods, fields, and exhaustive switch support.

Definition
enum Status { OPEN, CLOSED }
// Enums can have fields, methods, constructors
✅ Works
enum Status { OPEN, CLOSED }
String label = switch (s) {
  case OPEN -> "Open";    // ✅
  case CLOSED -> "Closed"; // ✅
};
Status.valueOf("OPEN"); // ✅
❌ Does Not Work
close("OPEN"); // ❌ String ≠ Status
Status.valueOf("PENDING"); // ❌ IllegalArgumentException

C# Enums (Value Types)

Int-backed value types. Flags attribute for bitwise combinations.

Definition
enum Status { Open, Closed }
[Flags] enum Perms { Read = 1, Write = 2, Exec = 4 }
✅ Works
enum Status { Open, Closed }
string Label(Status s) => s switch {
  Status.Open => "Open",     // ✅
  Status.Closed => "Closed", // ✅
  _ => "Unknown"
};

// Flags enum
[Flags] enum Perms { Read=1, Write=2, Exec=4 }
var rw = Perms.Read | Perms.Write; // ✅ bitmask
❌ Does Not Work
Status s = (Status)99; // ⚠️ compiles but invalid value!
// C# enums are backed by int — no compile-time safety
// for out-of-range casts

Go iota Constants

Typed constants with iota auto-increment. No real enum type safety.

Definition
type Status int
const (
  Open   Status = iota // 0
  Closed               // 1
)
✅ Works
type Status int
const (
  Open   Status = iota // 0
  Closed               // 1
)

func label(s Status) string {
  switch s {
  case Open: return "Open"     // ✅
  case Closed: return "Closed" // ✅
  }
  return "Unknown"
}
❌ Does Not Work
label(42) // compiles ✅ → produces "Unknown"
// ❌ No restriction on arbitrary int values
// Go "enums" are just typed ints — not exhaustive
11. Data Modeling / Records

TypeScript Type Aliases

Lightweight object shapes. No runtime presence — erased to plain objects.

Definition
type User = { id: string; name: string; email: string };
✅ Works
type User = { id: string; name: string; email: string };
const u: User = { id:"1", name:"Ana", email:"a@b" }; // ✅
const { name } = u; // ✅ destructure
const copy: User = { ...u, name: "Ana" }; // ✅ spread
❌ Does Not Work
const bad: User = { id: "1", name: "X" };
// ❌ Property 'email' is missing
console.log(typeof u); // "object" — no runtime type

Java Records (16+)

Immutable data carriers with auto-generated equals, hashCode, toString.

Definition
record User(String id, String name, String email) {}
✅ Works
record User(String id, String name, String email) {}
var u = new User("1", "Ana", "a@b"); // ✅
u.name(); // ✅ → "Ana"
u.equals(new User("1","Ana","a@b")); // ✅ → true
❌ Does Not Work
new User("1", "Ana"); // ❌ all fields mandatory
u.name = "X"; // ❌ record components are final

C# Records (9.0+)

Immutable records with value equality, with-expressions, and deconstruction.

Definition
record User(string Id, string Name, string Email);
✅ Works
record User(string Id, string Name, string Email);
var u = new User("1", "Ana", "a@b"); // ✅
var copy = u with { Name = "Ana" }; // ✅ non-destructive mutation
Console.WriteLine(u == copy); // ✅ → false (value equality)

// Deconstruct
var (id, name, email) = u; // ✅
❌ Does Not Work
u.Name = "X"; // ❌ init-only property

Go Structs

Plain value-type structs. No built-in immutability or generated methods.

Definition
type User struct { ID, Name, Email string }
✅ Works
type User struct { ID, Name, Email string }
u := User{ID: "1", Name: "Ana", Email: "a@b"} // ✅
fmt.Println(u.Name) // ✅

// Structs are value types — copy by default
copy := u        // ✅ deep copy
copy.Name = "Ana" // does not affect u
❌ Does Not Work
u := User{ID: "1", Name: "Ana"}
// ⚠️ compiles — Email is "" (zero value), not an error
// No way to enforce "all fields required" at compile time
12. Exceptions / Error Handling

TypeScript Unchecked Throw

All exceptions are unchecked. Catch type is unknown/any.

Definition
throw new Error("message");
// catch variable is unknown
✅ Works
try {
  throw new Error("fail");
} catch (e) {
  if (e instanceof Error) console.log(e.message); // ✅
}
❌ Does Not Work
try {} catch (e: Error) {}
// ❌ Catch variable must be 'any' or 'unknown'
// Compiler does NOT force try/catch

Java Checked + Unchecked Exceptions

Checked exceptions enforced by compiler. Unchecked for runtime errors.

Definition
void read() throws IOException { }
// Checked: must handle or declare
// Unchecked: RuntimeException subclasses
✅ Works
void read() throws IOException { throw new IOException(); }
try { read(); }
catch (IOException e) { e.printStackTrace(); } // ✅
// Compiler FORCES handling of checked exceptions
❌ Does Not Work
void caller() { read(); }
// ❌ unreported exception IOException

C# Unchecked Exceptions

All exceptions unchecked. Exception filters with when clause.

Definition
throw new InvalidOperationException("fail");
// All exceptions unchecked — no forced handling
✅ Works
try { throw new InvalidOperationException("fail"); }
catch (InvalidOperationException ex) {
  Console.WriteLine(ex.Message); // ✅
}

// Exception filters
try { DoWork(); }
catch (Exception ex) when (ex.Message.Contains("timeout")) {
  // ✅ only catches timeout exceptions
}
❌ Does Not Work
// No checked exceptions — compiler never forces handling
DoRiskyWork(); // compiles ✅ → may throw at runtime
// Must rely on documentation and convention

Go Error Values (no exceptions)

Errors are values, not exceptions. No try/catch.

Definition
func read() (string, error) {
  return "", fmt.Errorf("fail")
}
✅ Works
func read() (string, error) {
  return "", fmt.Errorf("fail")
}

data, err := read()
if err != nil {
  log.Println(err) // ✅ explicit error check
}

// errors.Is / errors.As for wrapping
if errors.Is(err, os.ErrNotExist) { } // ✅
❌ Does Not Work
data, _ := read() // compiles ✅ — silently ignores error
// ❌ Bad practice but compiler allows it

// No try/catch — cannot use Java/C# style
try { read() } // ❌ syntax error
13. Runtime Type Model

TypeScript Types Erased

All type information removed at compile time. Runtime uses JS typeof/instanceof.

Definition
// All types removed at compile time
typeof val      // JS: "string", "number", "object"
val instanceof Class // only for JS classes
✅ Works
// User-defined type guard
function isUser(o: unknown): o is User {
  return typeof o === "object" && o !== null && "name" in o;
}
if (isUser(val)) console.log(val.name); // ✅
❌ Does Not Work
if (val instanceof User) {} // ❌ 'User' is a type, not a value
typeof val === "User";       // ❌ typeof returns JS primitives

Java Full Runtime Metadata

Class metadata preserved. instanceof and reflection available. Generic erasure.

Definition
obj.getClass()           // → Class<?>
obj instanceof User      // pattern matching
User.class.getDeclaredFields() // reflection
✅ Works
if (obj instanceof User u) { u.name(); } // ✅
Class<?> c = obj.getClass();
c.getName(); // ✅ → "User"
User.class.getDeclaredConstructor().newInstance(); // ✅
❌ Does Not Work
if (obj instanceof List<String>) {} // ❌ generic erasure

C# Full Runtime Metadata (Reified)

Full runtime metadata including reified generics. Rich reflection and dynamic instantiation.

Definition
obj.GetType()           // → Type
obj is User u           // pattern matching
typeof(List<string>)   // generic info preserved
✅ Works
if (obj is User u) { Console.WriteLine(u.Name); } // ✅
Type t = obj.GetType();
Console.WriteLine(t.Name); // ✅ → "User"

// Generics preserved at runtime!
typeof(List<string>).GenericTypeArguments[0]; // ✅ → String

// Dynamic instantiation
Activator.CreateInstance<User>(); // ✅
❌ Does Not Work
// Unlike Java, almost everything works at runtime.
// Main limitation: anonymous types are internal

Go reflect Package

Type switches for interface dispatch. reflect package for structural inspection.

Definition
reflect.TypeOf(val)     // → reflect.Type
reflect.ValueOf(val)    // → reflect.Value
switch v := val.(type) {} // type switch
✅ Works
// Type switches
switch v := obj.(type) {
case User: fmt.Println(v.Name) // ✅
}

// reflect package
t := reflect.TypeOf(User{})
fmt.Println(t.Name()) // ✅ → "User"
t.NumField()          // ✅ → field count
❌ Does Not Work
// No generic type info at runtime (monomorphized)
reflect.TypeOf(Identity[int]) // ❌ doesn't carry [int]
14. Reflection & Metadata

TypeScript Decorators (Library)

Experimental decorators on classes/members. Requires reflect-metadata library.

Definition
@Log
class Service {
  @Validate method() {}
}
// Requires experimentalDecorators + reflect-metadata
✅ Works
class UserService {
  @Log getUser(id: string) { return { id }; }
}
// ✅ with decorator + reflect-metadata library
❌ Does Not Work
@Log function standalone() {} // ❌ decorators only on classes/members
Object.keys(User); // ❌ type-only — no runtime presence

Java Built-in Annotations + Reflection

First-class annotations with configurable retention. Full reflection API.

Definition
@Retention(RetentionPolicy.RUNTIME)
@interface Log {}
// Annotations inspectable via reflection API
✅ Works
@Retention(RetentionPolicy.RUNTIME)
@interface Log {}

Method m = Svc.class.getMethod("getUser", String.class);
m.isAnnotationPresent(Log.class); // ✅
Field[] fields = User.class.getDeclaredFields(); // ✅
❌ Does Not Work
@Retention(RetentionPolicy.SOURCE) @interface X {}
m.isAnnotationPresent(X.class); // → false
// ❌ SOURCE retention discarded after compilation

C# Attributes + Rich Reflection

Compile-time attributes with runtime reflection. Source generators for code gen.

Definition
[AttributeUsage(AttributeTargets.Method)]
class LogAttribute : Attribute {}
// Attributes are compile-time constant metadata
✅ Works
[AttributeUsage(AttributeTargets.Method)]
class LogAttribute : Attribute {}

class Svc { [Log] public void Run() {} }

var attr = typeof(Svc).GetMethod("Run")!
  .GetCustomAttribute<LogAttribute>(); // ✅

// Source generators — compile-time code gen
[JsonSerializable(typeof(User))]
partial class MyContext : JsonSerializerContext {} // ✅
❌ Does Not Work
// Attributes must be compile-time constants
[Log(DateTime.Now)] // ❌ must be constant expression

Go Struct Tags + reflect

String-based struct tags parsed at runtime. No decorator/attribute system.

Definition
type User struct {
  Name string `json:"name" validate:"required"`
}
// Tags are string metadata on struct fields
✅ Works
type User struct {
  Name  string `json:"name" validate:"required"`
  Email string `json:"email"`
}

t := reflect.TypeOf(User{})
field, _ := t.FieldByName("Name")
fmt.Println(field.Tag.Get("json")) // ✅ → "name"
❌ Does Not Work
// Tags are strings — no compile-time validation
type Bad struct {
  X string `json:"x" jsson:"typo"` // compiles ✅ — no warning
}
// ❌ No decorator/attribute system — struct tags are the only option
15. Modules & Packaging

TypeScript ES Modules

ES module syntax with type-only imports. Tree-shakeable by bundlers.

Definition
import { greet } from "./utils";
import type { User } from "./models";
export function hello() {}
✅ Works
import { greet } from "./utils"; // ✅
import type { User } from "./models"; // ✅ type-only
export { greet } from "./utils"; // ✅ re-export
❌ Does Not Work
greet = () => "x"; // ❌ imports are read-only
import { missing } from "./utils"; // ❌ no such export

Java Packages + JPMS

Package hierarchy plus Java Platform Module System for encapsulation.

Definition
package com.example.app;
import com.example.utils.Greeter;
module com.app { requires com.lib; }
✅ Works
import com.example.utils.Greeter; // ✅
import static com.example.utils.Greeter.greet; // ✅
module com.app { requires com.lib; exports com.api; } // ✅
❌ Does Not Work
import com.example.internal.Secret;
// ❌ not exported by module

C# Namespaces + Assemblies

Namespace organization with assembly-level access control.

Definition
namespace MyApp.Services;
using System.Text.Json;
global using System.Linq; // C# 10
✅ Works
using System.Text.Json;       // ✅ namespace import
using static Math;             // ✅ static import
global using System.Linq;     // ✅ file-scoped (C# 10)
using JsonAlias = System.Text.Json.JsonSerializer; // ✅ alias
❌ Does Not Work
// Cannot access internal types from another assembly
new SomeLibrary.InternalHelper();
// ❌ inaccessible due to its protection level

Go Packages + Go Modules

Directory-based packages. Case-based visibility. Strict unused import enforcement.

Definition
package main
import "fmt"
import "github.com/user/repo/pkg"
✅ Works
import "fmt"                      // ✅ stdlib
import "github.com/user/repo/pkg" // ✅ external
import _ "embed"                  // ✅ side-effect only

// Exported = starts with uppercase
func Greet() string { return "Hi" } // ✅ public
❌ Does Not Work
import "fmt" // ❌ imported and not used (compile error!)
// Go enforces all imports must be used

pkg.helper() // ❌ lowercase = unexported/private
16. Concurrency Types

TypeScript Promise / async-await

Single-threaded event loop. Promises for async operations.

Definition
async function fetch(): Promise<Data> { }
const result = await fetch();
Promise.all([p1, p2]); // parallel
✅ Works
async function fetchUser(id: string): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  return res.json(); // ✅ typed Promise
}

// Promise.all for concurrency
const [a, b] = await Promise.all([fetchUser("1"), fetchUser("2")]); // ✅
❌ Does Not Work
const user: User = fetchUser("1");
// ❌ Type 'Promise<User>' not assignable to 'User'
// Must await

Java CompletableFuture / Virtual Threads

CompletableFuture for async composition. Virtual threads for lightweight concurrency.

Definition
CompletableFuture<T> future = CompletableFuture
  .supplyAsync(() -> compute());
Thread.startVirtualThread(() -> { }); // Java 21+
✅ Works
CompletableFuture<User> future = CompletableFuture
  .supplyAsync(() -> fetchUser("1")); // ✅
User u = future.join(); // ✅

// Virtual threads (Java 21+)
Thread.startVirtualThread(() -> {
  var user = fetchUser("1"); // ✅ blocking but lightweight
});
❌ Does Not Work
User u = CompletableFuture.supplyAsync(() -> fetchUser("1"));
// ❌ CompletableFuture<User> ≠ User — must .join() or .get()

C# Task / async-await + Channels

Task-based async model with compiler-generated state machines. Channel support.

Definition
async Task<T> FetchAsync() { }
var result = await FetchAsync();
var ch = Channel.CreateUnbounded<int>();
✅ Works
async Task<User> FetchUser(string id) {
  var res = await httpClient.GetAsync($"/api/users/{id}");
  return await res.Content.ReadFromJsonAsync<User>(); // ✅
}

// Parallel
var tasks = ids.Select(FetchUser);
User[] users = await Task.WhenAll(tasks); // ✅

// Channels (like Go)
var ch = Channel.CreateUnbounded<int>();
await ch.Writer.WriteAsync(42); // ✅
❌ Does Not Work
User u = FetchUser("1");
// ❌ Cannot convert Task<User> to User — must await

async void BadMethod() { } // ⚠️ fire-and-forget — exceptions lost

Go Goroutines + Channels

Built-in concurrency primitives — no async/await needed.

Definition
ch := make(chan int)
go func() { ch <- 42 }()
result := <-ch
✅ Works
ch := make(chan User)

go func() {
  ch <- fetchUser("1") // ✅ send to channel
}()

user := <-ch // ✅ receive (blocks until ready)

// Select for multiplexing
select {
case u := <-ch1: fmt.Println(u) // ✅
case u := <-ch2: fmt.Println(u) // ✅
case <-time.After(5 * time.Second): fmt.Println("timeout")
}
❌ Does Not Work
ch := make(chan int)
ch <- 1 // ❌ deadlock — unbuffered channel, no receiver

// Goroutine leaks — no built-in cancellation
go func() { for { /* ... */ } }() // ⚠️ runs forever if not cancelled
17. Value Types & Memory Model

TypeScript All Reference (JS)

Objects are always references. Primitives (number, string, boolean) are value types at JS level.

Definition
// Primitives: number, string, boolean (value copy)
// Objects: always reference
const obj = { x: 1 }; // reference type
✅ Works
const a = { x: 1 };
const b = a;
b.x = 2;
console.log(a.x); // ✅ → 2 (same reference)

const n = 42;
let m = n;
m = 99;
console.log(n); // ✅ → 42 (primitive = value copy)

Java Primitives + Reference Types

Primitives (int, double, etc.) are values. Objects are references.

Definition
int n = 42;            // primitive (stack)
Integer boxed = n;     // autoboxed (heap)
User u = new User();   // reference (heap)
✅ Works
int a = 42;
int b = a; // value copy
b = 99;
System.out.println(a); // ✅ → 42

// Objects are references
User u1 = new User("1","A","a@b");
User u2 = u1; // same ref
❌ Does Not Work
// Cannot create custom value types (until Valhalla)
// Everything non-primitive lives on the heap

C# struct (Value) vs class (Reference)

C# lets you choose value or reference semantics.

Definition
struct Point { public int X, Y; } // value type
class User { }                    // reference type
Span<int> s = stackalloc int[3]; // stack-only
✅ Works
struct Point { public int X, Y; }
Point a = new(1, 2);
Point b = a; // ✅ full copy
b.X = 99;
Console.WriteLine(a.X); // ✅ → 1 (independent copy)

// Span<T> for stack-allocated slices
Span<int> span = stackalloc int[] { 1, 2, 3 }; // ✅ no heap

// ref struct — guaranteed stack-only
ref struct FastBuffer { public Span<byte> Data; }
❌ Does Not Work
ref struct Bad { }
Task.Run(() => { Bad b; });
// ❌ ref struct cannot be captured in lambda/async

Go Structs = Values, Pointers Explicit

All structs are value types. Use pointers for reference semantics.

Definition
type Point struct { X, Y int } // value type
var p *Point = &Point{1, 2}  // pointer for ref semantics
// All structs copied by default
✅ Works
type Point struct { X, Y int }
a := Point{1, 2}
b := a // ✅ full copy
b.X = 99
fmt.Println(a.X) // ✅ → 1

// Pointer for shared reference
p := &a
p.X = 50
fmt.Println(a.X) // ✅ → 50

// Slices are reference-like (header + pointer to array)
s := []int{1, 2, 3} // ✅ backed by array on heap
❌ Does Not Work
// Large structs copied on every function call — can be expensive
func process(u User) { } // copies entire User struct
// Use *User for large structs to avoid copy overhead
18. Interface Composition & Embedding

TypeScript Intersection Types

Combine types with & operator. Extend interfaces with extends.

Definition
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge;
✅ Works
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge;

const p: Person = { name: "Ana", age: 30 }; // ✅

// Extend interfaces
interface Animal { name: string; }
interface Pet extends Animal { owner: string; }
❌ Does Not Work
type Conflict = { x: string } & { x: number };
// ❌ x becomes 'never' — string & number is impossible

Java Interface extends

Interfaces extend multiple interfaces. Classes implement multiple interfaces.

Definition
interface HasName { String getName(); }
interface HasAge { int getAge(); }
interface Person extends HasName, HasAge {}
✅ Works
interface HasName { String getName(); }
interface HasAge { int getAge(); }
interface Person extends HasName, HasAge {} // ✅

// Class implements multiple interfaces
class User implements HasName, HasAge {
  public String getName() { return "Ana"; } // ✅
  public int getAge() { return 30; } // ✅
}
❌ Does Not Work
class A { void run() {} }
class B { void run() {} }
class C extends A, B {} // ❌ no multiple inheritance of classes

C# Interface + Default Methods

Multiple interface inheritance with default method implementations.

Definition
interface IHasName { string Name { get; } }
interface IHasAge { int Age { get; } }
interface IPerson : IHasName, IHasAge {}
✅ Works
interface IHasName { string Name { get; } }
interface IHasAge { int Age { get; } }
interface IPerson : IHasName, IHasAge {} // ✅

// Default interface methods (C# 8+)
interface ILogger {
  void Log(string msg);
  void LogError(string msg) => Log($"ERROR: {msg}"); // ✅ default
}
❌ Does Not Work
class A { } class B { }
class C : A, B { } // ❌ no multiple class inheritance

Go Embedding (Composition over Inheritance)

No inheritance. Composition via struct and interface embedding.

Definition
type Reader interface { Read(p []byte) (int, error) }
type Writer interface { Write(p []byte) (int, error) }
type ReadWriter interface { Reader; Writer }
✅ Works
// Interface embedding
type Reader interface { Read(p []byte) (int, error) }
type Writer interface { Write(p []byte) (int, error) }
type ReadWriter interface { Reader; Writer } // ✅ composed

// Struct embedding — "inherits" methods
type Animal struct { Name string }
func (a Animal) Speak() string { return a.Name }

type Dog struct {
  Animal // ✅ embedded — Dog gets Speak() for free
  Breed string
}
d := Dog{Animal{"Rex"}, "Lab"}
d.Speak() // ✅ → "Rex"
❌ Does Not Work
// Ambiguous embedding — two embedded types with same method
type A struct{}; func (A) Do() {}
type B struct{}; func (B) Do() {}
type C struct { A; B }
c := C{}
c.Do() // ❌ ambiguous selector C.Do
19. Conditional Types

TypeScript Conditional Type Expressions

Types that branch based on assignability checks. Distributive over unions.

Definition
type IsString<T> = T extends string ? "yes" : "no";
type NonNullable<T> = T extends null | undefined ? never : T;
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
✅ Works
type A = IsString<string>;  // ✅ → "yes"
type B = IsString<number>;  // ✅ → "no"

// infer keyword extracts types
type Unpacked<T> = T extends Promise<infer U> ? U : T;
type C = Unpacked<Promise<string>>; // ✅ → string

// Distributive over unions
type D = NonNullable<string | null | number>; // ✅ → string | number

// Flatten arrays recursively
type Flat<T> = T extends Array<infer U> ? Flat<U> : T;
type E = Flat<number[][][]>; // ✅ → number
❌ Does Not Work
type Bad = string extends T ? "y" : "n";
// ❌ T not in scope — conditional types need a generic context

type Loop<T> = T extends any ? Loop<T> : never;
// ❌ Type instantiation is excessively deep

Java No Equivalent

No type-level conditionals. Solved via overloads, instanceof, or sealed hierarchies.

Definition
// No type-level conditional — use runtime dispatch
sealed interface Result<T> {}
record Ok<T>(T value) implements Result<T> {}
record Err<T>(Exception error) implements Result<T> {}
✅ Works (runtime pattern matching)
String unwrap(Result<String> r) {
  return switch (r) {
    case Ok<String> ok -> ok.value();  // ✅
    case Err<String> e -> "error";     // ✅
  };
}
❌ Does Not Work
// Cannot express: "if T extends X then Y else Z" at type level
// Must duplicate code or use runtime checks

C# No Equivalent (Constraints Only)

Generic constraints (where T : ...) limit but don't branch types.

Definition
// Constraints restrict, not branch
T Process<T>(T val) where T : IComparable<T> => val;

// No type-level conditional — use overloads
string Format(int n) => n.ToString();
string Format(string s) => s;
✅ Works (overload resolution)
Format(42);     // ✅ → calls int overload
Format("hello"); // ✅ → calls string overload

// Static abstract interface members (C# 11) — closer to type-level dispatch
interface IAddable<T> where T : IAddable<T> {
  static abstract T operator +(T a, T b);
}
❌ Does Not Work
// Cannot express: type X = T is string ? A : B
// No type-level branching or infer keyword

Go No Equivalent

No type-level conditionals. Use type switches or interface constraints.

Definition
// Constraint unions (closest to conditional)
type Number interface { int | float64 }

func Double[T Number](v T) T { return v + v }
✅ Works (constraint unions)
Double(42)    // ✅ → 84
Double(3.14)  // ✅ → 6.28

// Runtime type switch
func describe(v any) string {
  switch v.(type) {
  case string: return "string" // ✅
  case int: return "int"       // ✅
  default: return "other"
  }
}
❌ Does Not Work
// Cannot express type-level if/else
// Cannot infer inner types from generics
// No way to extract element type from []T at type level
20. Type Narrowing & Guards

TypeScript Control-flow Narrowing

Compiler tracks type through control flow. Custom type predicates.

Definition
function isString(v: unknown): v is string {
  return typeof v === "string";
}
// typeof, instanceof, in, ===, custom predicates
✅ Works
function process(val: string | number | null) {
  if (val === null) return;         // narrowed: string | number
  if (typeof val === "string") {
    console.log(val.toUpperCase()); // ✅ narrowed to string
  } else {
    console.log(val.toFixed(2));    // ✅ narrowed to number
  }
}

// "in" operator narrows
type A = { kind: "a"; x: number };
type B = { kind: "b"; y: string };
function handle(v: A | B) {
  if ("x" in v) console.log(v.x); // ✅ narrowed to A
}

// Discriminated union exhaustiveness
type Shape = { kind: "circle"; r: number } | { kind: "rect"; w: number; h: number };
function area(s: Shape): number {
  switch (s.kind) {
    case "circle": return Math.PI * s.r ** 2; // ✅
    case "rect": return s.w * s.h;             // ✅
    // compiler warns if case missing (with never check)
  }
}
❌ Does Not Work
function bad(val: string | number) {
  console.log(val.toUpperCase());
  // ❌ 'toUpperCase' does not exist on type 'number'
}

// Narrowing doesn't survive callbacks
function tricky(v: string | null) {
  if (v !== null) {
    setTimeout(() => {
      v.toUpperCase(); // ⚠️ may warn — closure doesn't preserve narrowing
    });
  }
}

Java Pattern Matching instanceof (16+)

instanceof with binding variable. Switch pattern matching (21+).

Definition
if (obj instanceof String s) { /* s is String */ }
// switch patterns (Java 21+)
switch (obj) {
  case String s -> s.length();
  case Integer i -> i * 2;
}
✅ Works
Object val = "hello";
if (val instanceof String s) {
  System.out.println(s.toUpperCase()); // ✅ bound + narrowed
}

// Guarded patterns
if (val instanceof String s && s.length() > 3) {
  System.out.println(s); // ✅ both narrowed and filtered
}

// Sealed type exhaustiveness (21+)
sealed interface Shape permits Circle, Rect {}
String desc(Shape s) {
  return switch (s) {
    case Circle c -> "r=" + c.r();  // ✅
    case Rect r -> r.w() + "x" + r.h(); // ✅
  }; // exhaustive — compiler checks
}
❌ Does Not Work
Object val = "hello";
val.toUpperCase(); // ❌ Object has no toUpperCase
// Must pattern match or cast first

// No flow-based narrowing like TS
if (val != null) {
  val.toString(); // ✅ but val is still typed as Object
}

C# Pattern Matching Expressions

Rich pattern syntax: type, property, relational, list patterns.

Definition
if (obj is string s) { /* s is string */ }
// Property patterns
if (obj is User { Age: > 18 } adult) { }
// Switch expressions
var result = obj switch { string s => s, int i => i.ToString(), _ => "" };
✅ Works
object val = "hello";
if (val is string s) {
  Console.WriteLine(s.ToUpper()); // ✅ narrowed
}

// Relational + logical patterns
string Classify(int n) => n switch {
  < 0 => "negative",    // ✅
  0 => "zero",           // ✅
  > 0 and < 100 => "small", // ✅ combined
  _ => "large"
};

// List patterns (C# 11)
int[] arr = { 1, 2, 3 };
if (arr is [1, _, 3]) { } // ✅ matches first=1, any, last=3

// Nested property patterns
if (user is { Address: { City: "Paris" } }) { } // ✅ deep match
❌ Does Not Work
object val = "hello";
Console.WriteLine(val.Length);
// ❌ 'object' does not contain 'Length' — must pattern match first

Go Type Assertions & Switches

Runtime type assertions on interfaces. No compile-time narrowing.

Definition
// Type assertion
s := val.(string)        // panics if wrong
s, ok := val.(string)    // safe — ok is bool

// Type switch
switch v := val.(type) {
case string: // v is string
case int:    // v is int
}
✅ Works
func describe(val any) string {
  switch v := val.(type) {
  case string: return "string: " + v       // ✅ v is string
  case int:    return fmt.Sprintf("int: %d", v) // ✅ v is int
  case nil:    return "nil"                 // ✅
  default:     return "unknown"
  }
}

// Safe assertion with comma-ok
if s, ok := val.(string); ok {
  fmt.Println(len(s)) // ✅ s is string
}
❌ Does Not Work
var val any = "hello"
fmt.Println(val + " world")
// ❌ operator + not defined on any

s := val.(int) // ❌ panic: interface conversion: string, not int

// No flow-based narrowing — type check doesn't narrow outside switch
if _, ok := val.(string); ok { }
// val is still 'any' here, not string
21. Extension Methods & Operator Overloading

TypeScript Module Augmentation / No Operator Overloading

Extend existing types via declaration merging. No operator overloading.

Definition
// Declaration merging — extend existing interface
declare global {
  interface Array<T> {
    first(): T | undefined;
  }
}
Array.prototype.first = function() { return this[0]; };
✅ Works
const arr = [1, 2, 3];
arr.first(); // ✅ → 1 (prototype extension)

// Module augmentation
declare module "express" {
  interface Request { userId?: string; }
}
// Now req.userId is typed ✅
❌ Does Not Work
// No operator overloading
class Vec { constructor(public x: number, public y: number) {} }
const v = new Vec(1,2) + new Vec(3,4);
// ❌ operator + cannot be applied to Vec

// Prototype pollution risk
Array.prototype.first = /* ... */; // ⚠️ affects all arrays globally

Java No Extensions / No Operator Overloading

Cannot add methods to existing types. No operator overloading.

Definition
// Static utility methods instead
class StringUtils {
  static boolean isBlank(String s) {
    return s == null || s.trim().isEmpty();
  }
}
✅ Works
StringUtils.isBlank("  "); // ✅ → true
StringUtils.isBlank("hi"); // ✅ → false

// Default methods on interfaces (Java 8+)
interface Greetable {
  String name();
  default String greet() { return "Hi " + name(); } // ✅
}
❌ Does Not Work
"hello".isBlank(); // ⚠️ only in Java 11+ (added to String class)
// Cannot add custom methods to String or any existing class

// No operator overloading
BigDecimal a = new BigDecimal("1.5");
BigDecimal b = a + a; // ❌ must use a.add(a)

C# Extension Methods + Operator Overloading

Add methods to any type without modifying it. Full operator overloading.

Definition
// Extension method
static class StringExt {
  public static bool IsBlank(this string s) =>
    string.IsNullOrWhiteSpace(s);
}

// Operator overloading
record Vec(double X, double Y) {
  public static Vec operator +(Vec a, Vec b) =>
    new(a.X + b.X, a.Y + b.Y);
}
✅ Works
"  ".IsBlank();  // ✅ → true (extension method)
"hi".IsBlank();  // ✅ → false

var v = new Vec(1, 2) + new Vec(3, 4); // ✅ → Vec(4, 6)

// LINQ is built entirely on extension methods
var names = users.Where(u => u.Age > 18).Select(u => u.Name); // ✅
❌ Does Not Work
// Extension methods can't access private members
static class Bad {
  public static int GetSecret(this User u) => u._secret;
  // ❌ '_secret' is inaccessible due to its protection level
}

// Can't override existing methods via extensions
// The real method always wins over the extension

Go Methods on Own Types / No Operator Overloading

Add methods only to types in the same package. No operator overloading.

Definition
// Methods on your own types
type MyString string

func (s MyString) IsBlank() bool {
  return strings.TrimSpace(string(s)) == ""
}
✅ Works
var s MyString = "  "
s.IsBlank() // ✅ → true

// Type alias trick for extending
type Strings []string
func (ss Strings) First() string {
  if len(ss) == 0 { return "" }
  return ss[0]
}
Strings{"a", "b"}.First() // ✅ → "a"
❌ Does Not Work
// Cannot add methods to types from other packages
func (s string) IsBlank() bool { }
// ❌ cannot define new methods on non-local type string

// No operator overloading
type Vec struct{ X, Y float64 }
v := Vec{1,2} + Vec{3,4}
// ❌ operator + not defined on struct type Vec
22. Immutability Patterns

TypeScript readonly & const Assertions

Compile-time immutability via readonly modifier and const assertions.

Definition
type User = { readonly name: string; readonly age: number };
const ROLES = ["admin", "user"] as const;
// typeof ROLES = readonly ["admin", "user"]
✅ Works
const u: User = { name: "Ana", age: 30 };
console.log(u.name); // ✅

// Deep readonly via utility
type DeepReadonly<T> = { readonly [K in keyof T]: DeepReadonly<T[K]> };

// ReadonlyArray
function process(items: ReadonlyArray<string>) {
  items[0]; // ✅ read
}

// Object.freeze at runtime
const config = Object.freeze({ host: "localhost", port: 3000 });
❌ Does Not Work
u.name = "Bob";
// ❌ Cannot assign to 'name' — readonly property

const arr: ReadonlyArray<number> = [1, 2];
arr.push(3); // ❌ Property 'push' does not exist on ReadonlyArray

// ⚠️ readonly is compile-time only — no runtime enforcement
// Object.freeze is shallow

Java final + Unmodifiable Collections

final prevents reassignment. Records are shallow-immutable. Collections wrapped.

Definition
final int x = 42;              // cannot reassign
record Point(int x, int y) {}  // immutable components
List<String> list = List.of("a", "b"); // unmodifiable
✅ Works
record Config(String host, int port) {}
var c = new Config("localhost", 3000); // ✅ immutable
c.host(); // ✅ → "localhost"

// Unmodifiable collections
var names = List.of("Ana", "Bob"); // ✅
names.get(0); // ✅ → "Ana"

// Collections.unmodifiableList wraps mutable list
var safe = Collections.unmodifiableList(mutableList); // ✅
❌ Does Not Work
final int x = 42;
x = 99; // ❌ cannot assign to final variable

names.add("C"); // ❌ UnsupportedOperationException at runtime

// ⚠️ final on reference doesn't make object immutable
final List<String> list = new ArrayList<>();
list.add("x"); // ✅ reference is final, but contents aren't

C# init-only, readonly, Records

Multiple immutability levels: init, readonly, records with non-destructive mutation.

Definition
record Config(string Host, int Port);  // immutable record
readonly struct Point { public readonly int X, Y; }
class Options { public string Name { get; init; } } // init-only
✅ Works
var c = new Config("localhost", 3000); // ✅
var c2 = c with { Port = 8080 };       // ✅ non-destructive mutation

// ImmutableArray / ImmutableList
var list = ImmutableList.Create("a", "b"); // ✅
var list2 = list.Add("c"); // ✅ new list, original unchanged

// Frozen collections (NET 8)
var frozen = names.ToFrozenSet(); // ✅ optimized for reads
❌ Does Not Work
c.Host = "other";
// ❌ init-only property — cannot set after construction

var opt = new Options { Name = "X" };
opt.Name = "Y"; // ❌ init-only

Go No Built-in Immutability

No const for composite types. Use unexported fields and getter methods.

Definition
// const only for primitives
const MaxRetries = 3

// Immutability via encapsulation
type Config struct {
  host string // unexported = read-only from outside
  port int
}
func (c Config) Host() string { return c.host }
✅ Works
c := Config{host: "localhost", port: 3000}
c.Host() // ✅ → "localhost" (via getter)

// Value semantics — copies are independent
c2 := c        // full copy
c2.port = 8080 // doesn't affect c ✅

// Convention: return new value instead of mutating
func withPort(c Config, port int) Config {
  c.port = port
  return c // ✅ returns new copy
}
❌ Does Not Work
const config = Config{} // ❌ const only for basic types
// No way to make struct truly immutable at compile time

// Slices/maps are reference-like even in "immutable" struct
type Bad struct { items []string }
b := Bad{items: []string{"a"}}
copy := b
copy.items[0] = "x" // ⚠️ mutates original too!