TS
C#

TypeScript to C#

10 lessons

Progress0%
1Introduction: Two Languages, One Mind2Type Systems: Structural vs Nominal3Classes: Advanced Features4Generics and LINQ5Nullable Reference Types6Async/Await7Decorators to Attributes8Ecosystem9File I/O10Records and Pattern Matching
All Mirror Courses
TS
C#
Classes: Advanced Features
MirrorLesson 3 of 10
Lesson 3

Classes: Advanced Features

Classes & OOP

Introduction

In this lesson, you'll learn about classes: advanced features in C#. Coming from TypeScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
TS
From TypeScript:

In TypeScript, you're familiar with classes & oop.

C#
In C#:

C# has its own approach to classes & oop, which we'll explore step by step.

The C# Way

Let's see how C# handles this concept. Here's a typical example:

C#
C# Example
// C# 9 record — immutable data class
record User(int Id, string Name);

// C# 12 — primary constructors
class Product(string Sku, string Name, decimal Price) {
    public string Sku { get; } = Sku;
    public string Name { get; set; } = Name;
    private decimal Price { get; } = Price;
}

// C# 9 init-only setters
class Config {
    public string Host { get; init; } = "localhost";
    public int Port { get; init; } = 8080;
}

// Object initializer with init
var cfg = new Config { Host = "prod.example.com", Port = 443 };

Comparing to TypeScript

Here's how you might have written similar code in TypeScript:

TS
TypeScript (What you know)
class User {
  readonly id: number;
  name: string;

  constructor(id: number, name: string) {
    this.id = id;
    this.name = name;
  }
}

// TypeScript 5 — parameter properties
class Product {
  constructor(
    public readonly sku: string,
    public name: string,
    private price: number,
  ) {}
}
Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

C#
In C#:

C# record provides immutable value-semantic classes with less boilerplate

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

C#
In C#:

C# init-only setters allow construction-time assignment but prevent later mutation

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

C#
In C#:

C# primary constructors (C# 12) are similar to TypeScript parameter properties

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

C#
In C#:

required keyword in C# enforces that a property is set during construction

Step-by-Step Breakdown

1. record = Immutable Data Class

C# record generates constructor, ToString, Equals, GetHashCode, and deconstruction automatically. It's the C# equivalent of TypeScript readonly interfaces with less code.

TS
TypeScript
interface User { readonly id: number; readonly name: string; }
C#
C#
record User(int Id, string Name);
// Auto-generates: equality, ToString, deconstruct
var u = new User(1, "Alice");
var (id, name) = u; // deconstruction

2. init-only Properties

init replaces set in property definitions. The value can only be assigned at construction time (new MyClass { Prop = value }) — cannot be changed after.

TS
TypeScript
class Config {
  readonly host: string = "localhost";
}
C#
C#
class Config {
    public string Host { get; init; } = "localhost";
}
// OK:
var c = new Config { Host = "prod" };
// Error:
// c.Host = "other"; // init-only!
Rule of Thumb
Use init instead of set when you want a property to be configurable during construction but immutable afterward.

3. required Properties

The required keyword forces callers to set the property in an object initializer. The compiler errors if it's missing — similar to TypeScript's required fields.

TS
TypeScript
interface CreateUser { name: string; email: string; } // all required
C#
C#
class CreateUserDto {
    public required string Name { get; init; }
    public required string Email { get; init; }
}
// new CreateUserDto() // Error: Name, Email required

4. with Expression — Non-Destructive Mutation

Records support the with expression to create a copy with some properties changed — equivalent to TypeScript's spread: { ...obj, name: 'new' }.

TS
TypeScript
const updated = { ...user, name: "Bob" };
C#
C#
var updated = user with { Name = "Bob" };
// Original user is unchanged

Common Mistakes

When coming from TypeScript, developers often make these mistakes:

  • C# record provides immutable value-semantic classes with less boilerplate
  • C# init-only setters allow construction-time assignment but prevent later mutation
  • C# primary constructors (C# 12) are similar to TypeScript parameter properties
Common Pitfall
Don't assume C# works exactly like TypeScript. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • C# record is the go-to for immutable data — generates equality, ToString, and deconstruction
  • init-only setters allow construction-time assignment, preventing later mutation
  • required properties enforce that callers must set them in object initializers
  • with expression creates record copies with changed properties (like spread in TS)
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your TypeScript code in C# to practice these concepts.
PreviousNext