I think there is a practical advantage to oop when there are a low number of callers, such as in stateful parts of your application. JS libraries however have many many callers, and so can invest more effort in a non oop impl that provides a more ergonomic/pluggable interface. Also store-based state management is explicitly about centralizing state (i.e. not in a class), so it makes sense that this repo would not use oop to implement it. As someone that writes mainly application code, I do not use private class members, just private access in typescript. It's good enough to prevent erroneous calls within the application, while still being easy to debug/mock and more readable. Answer from aighball on reddit.com
🌐
TypeScript
typescriptlang.org › docs › handbook › 2 › classes.html
TypeScript: Documentation - Classes
Since TypeScript 4.3, it is possible to have accessors with different types for getting and setting. ... Classes can declare index signatures; these work the same as Index Signatures for other object types:
🌐
W3Schools
w3schools.com › typescript › typescript_classes.php
TypeScript Classes
When a class extends another class, it can replace the members of the parent class with the same name. Newer versions of TypeScript allow explicitly marking this with the override keyword.
Discussions

To`class` or not to `class`?
I think there is a practical advantage to oop when there are a low number of callers, such as in stateful parts of your application. JS libraries however have many many callers, and so can invest more effort in a non oop impl that provides a more ergonomic/pluggable interface. Also store-based state management is explicitly about centralizing state (i.e. not in a class), so it makes sense that this repo would not use oop to implement it. As someone that writes mainly application code, I do not use private class members, just private access in typescript. It's good enough to prevent erroneous calls within the application, while still being easy to debug/mock and more readable. More on reddit.com
🌐 r/typescript
49
15
April 26, 2023
angular - Is there a type for "Class" in Typescript? And does "any" include it? - Stack Overflow
In Java, you can give a class to a method as a parameter using the type "Class". I didn't find anything similar in the typescript docs - is it possible to hand a class to a method? And if so, does ... More on stackoverflow.com
🌐 stackoverflow.com
What is the difference between type and class in Typescript? - Stack Overflow
What are the situations where I would need to use type because class is not suitable? ... A TypeScript/JavaScript class is a function. A TypeScript type is just a definition that helps the TS compiler check the code. More on stackoverflow.com
🌐 stackoverflow.com
What's the best practice for when to use interface vs. class vs. type?
In personal projects, I use type for data and interface for behavior. Why? interface can be extended which I think fits better with behavior, type can be intersected which fits better with composibility of data. interface can be implicitly merged. interface Calculator { add: (a: number, b: number) => number } interface Calculator { sub: (a: number, b: number) => number } interface is mutable, type is immutable. I think that fits better with behavior being mutable and data being immutable. On team projects, I use whatever my team uses. More on reddit.com
🌐 r/typescript
20
25
October 20, 2021
🌐
Refine
refine.dev › home › blog › tutorials › essentials of typescript classes
Essentials of TypeScript Classes | Refine
January 13, 2025 - TypeScript classes are an extension of the ES6 classes of JavaScript, adding type annotations, access modifiers (public, private, protected), and other features such as readonly fields, static members, and parameter properties.
🌐
Ultimate Courses
ultimatecourses.com › blog › classes-vs-interfaces-in-typescript
Classes vs Interfaces in TypeScript - Ultimate Courses
February 7, 2023 - ... Classes and interfaces are ... type-checking in TypeScript. A class is a blueprint from which we can create objects that share the same configuration - properties and methods....
🌐
Reddit
reddit.com › r/typescript › to`class` or not to `class`?
r/typescript on Reddit: To`class` or not to `class`?
April 26, 2023 -

Watched this old YouTube classic recently: Brian Will's Object-Oriented Programming is Bad.

And was recently looking through this codebase https://github.com/tinyplex/tinybase and realized there is not a single class keyword used.

Interested in people's thoughts/drawbacks on no-classes vs classes in general, and perhaps using the TinyBase's codebase as a basis for evaluating/reviewing the no-classes approach on a real-world project. Would this codebase be easier to read/document/visualize if classes were used.

I also included a short example of the different approaches:

Function variable version

main.ts

const store: Store = createStore('foo')
store.foo()

store.ts

import {createStore as createStoreDecl} from './store.d.ts'

// export function createStore(foo: string): Store { <- alternative form
export async const createStore: createStoreDecl = (foo: string): Store {
  const bar = 1
  await initStuff()
  function printFoo() {
    console.log(foo, bar)
  }
  return {
    foo() { printFoo() },
  }
}

store.d.ts

/** Some docs */ 
export async function createStore(foo: string): Store;

export interface Store {
  foo()
}

Function version with exports

This version extracts a pure function and exports it.

export function printFoo(foo, bar) { console.log(foo, bar) }

export async function createStore(foo: string): Store {
  const bar = 1
  await initStuff()
  return {
    foo() { printFoo(foo, bar) },
  }
}

// Potentially export private stuff in a private variable like so:
// export const private = {printFoo}

Class version

main.ts

const store: Store = new Store('foo')
await store.init()
store.foo()

store.ts

class Store {
  #foo: string
  static #bar = 1
  constructor(foo: string) { this.foo = foo; }
  async init() { await initStuff() }
  /** Some docs */
  foo() { this.#printFoo() }
  #printFoo() { 
    const Clazz = <typeof Store>this.constructor
    const bar = Clazz.bar
    console.log(this.foo, bar) 
  }
}

My thoughts on no-classes:

Pros

  • Async constructors are nice.

  • new keyword is awkward and doesn't chain well.

  • Documentation/typing in a separate file doesn't clutter code. This is also possible for classes though.

Cons

  • Typedef for function version must be separate to method definition. Can be positive and negative.

  • Commas in object definitions are not as clean.

  • Instance vars are not accessible ever. For example const foo can never be accessed, compared to #private in classes which I think you can do with reflection. There have been several times I wanted to read or monkey-patch one of these variables when desparately debugging some third-party library.

  • Duplication of factory type definition when using const foo = createFoo() form and its messy needed to assign type to the variable and the function.

  • Classes make it easier to see all fields and methods and their visibility (private, static, etc.)

  • Functions declared in the factory body (i.e. printFoo) can name-clash with factory params.

  • Harder to test private factory-scope functions.

  • Inheritance is harder to document/visualize. In TinyBase they support custom Persisters via createCustomPersister, which could neatly be implemented using inheritance which would also allow automatic documentation. Would be interested to know pro/cons of this approach.

On the function version with exported functions:

Pros

Testable

The function's data dependencies are explicit in the params. In the previous example, it's not clear until examining the entire method, which fields it depends on.

Easier to extract out generic/reusable utility functions, which helps keep the custom business logic clear. We want as many pure functions (i.e. not touching shared state) as possible.

Tends to be closer to what my code looks like on my first-attempt, before refactoring to allow configuration and additional features. This makes it easy to understand.

A lot of this can still be done with classes though - you can always just create a function instead of a static.

Cons

Cannot enforce private visibility.

Can be verbose and repetitive having to write the same arguments and parameters for each call and definition.

Developer may end up passing in a "context" object as a single param to avoid the previous issue which ends up defeating the purpose, and we are back to potentially depending on all the fields in a class.

Top answer
1 of 15
11
I think there is a practical advantage to oop when there are a low number of callers, such as in stateful parts of your application. JS libraries however have many many callers, and so can invest more effort in a non oop impl that provides a more ergonomic/pluggable interface. Also store-based state management is explicitly about centralizing state (i.e. not in a class), so it makes sense that this repo would not use oop to implement it. As someone that writes mainly application code, I do not use private class members, just private access in typescript. It's good enough to prevent erroneous calls within the application, while still being easy to debug/mock and more readable.
2 of 15
10
Honestly, I don't like classes and I also don't like the hoops and reinventing-the-wheel that we go through when we avoid classes. But, I've settled on not using classes, despite the tedium of sometimes creating ad-hoc "pseudo-classes" with interfaces, private symbol tags, factory functions, etc. Here are the problems with classes, from my POV: No private constructors There is no such thing as a private constructor in JavaScript. So, if you need validation of some kind, the only option is to throw exceptions in the constructor, which is not part of the static type system. Classes are structurally typed sometimes and nominally typed other times If your class has any private fields, then TypeScript treats it as a nominal type. Otherwise, it's a structural type. Classes automatically create a corresponding interface, which isn't always the correct thing to do. Let's say I write a class called Foo. And then I write a function that accepts a foo: Foo argument. One might mistakenly assume that foo instanceof Foo should always return true in that function, but that's not correct. Because I can pass anything that implements the Foo interface into that function, even if it does not have Foo in its prototype chain. Class methods have incorrect type variance in TypeScript By default, functions and methods are bivariant in TypeScript, which is unsound, not type safe, and just plain incorrect. Since 2.6, TypeScript has a flag strictFunctionTypes (which is included in strict). However, the strictFunctionTypes feature does not apply to class methods. Nor does it apply to interface methods that are written with "method syntax". So, class methods will always have unsound type variance. An example with an interface: interface Foo { bar(x: number | string): void // bad type variance bar: (x: number | string) => void // good type variance } So, even though it's tedious and annoying to avoid classes, I just hate all of the edge cases and gotchas with classes, so I avoid them. I rather have consistent, predictable, tedium than something "convenient" but inconsistent.
🌐
Mimo
mimo.org › glossary › typescript › class
TypeScript Class: Syntax, Usage, and Examples
A TypeScript class defines a blueprint for creating objects with properties and methods. It allows you to encapsulate data, enforce structure, and implement object-oriented programming concepts like inheritance, polymorphism, and encapsulation.
Find elsewhere
🌐
Medium
davembush.medium.com › use-typescript-class-instead-of-interface-or-type-cfbfca6d98c4
Use TypeScript Class instead of Interface or Type | by Dave Bush | Medium
May 24, 2025 - Use TypeScript Class instead of Interface or Type Since TypeScript introduced Interfaces and Types, we’ve been getting lazy. It is so much easier to create an object that obeys an interface than it …
🌐
Total TypeScript
totaltypescript.com › books › total-typescript-essentials › classes
Classes | Total TypeScript
You can use the class keyword to define a class, and then create instances of that class using the new keyword. TypeScript adds a layer of static type checking to classes, which can help you catch errors and enforce structure in your code.
🌐
GeeksforGeeks
geeksforgeeks.org › typescript › classes-in-typescript
Classes in TypeScript - GeeksforGeeks
August 6, 2025 - Typescript inherits this feature from ES6. In class group of objects which have common properties. Class contains fields, methods, constructors, Blocks, Nested class and interface.
🌐
LogRocket
blog.logrocket.com › home › when and how to use interfaces and classes in typescript
When and how to use interfaces and classes in TypeScript - LogRocket Blog
June 4, 2024 - In object-oriented programming, a class is a blueprint or template by which we can create objects with specific properties and methods. Typescript provides additional syntax for type checking and converts code to clean JavaScript that runs on ...
Top answer
1 of 5
179

Typescript has two different universes that come into contact in some points: Value space and Type space. Type space is where types are defined and types get erased completely and don't exist at runtime. Value space contains values and will obviously exist at runtime.

What is a value? Value literals, variables, constants and parameters are obviously values. Functions and class declarations are also values as they do have a runtime object backing them up, namely the function object and the class constructor (also a function). Enums are also values as they are backed up by an object at runtime.

What is a type? Any definition with a type keyword is a type as well as interfaces, class declarations and enums

You will notice I mentioned class declarations in both spaces. Classes exist in both type space, and value space. This is why we can use them both in type annotations (let foo: ClassName) and in expressions (ex new ClassName()).

Enums also span both worlds, they also represent a type we can use in an annotation, but also the runtime object that will hold the enum.

Names in type space and value space don't collide, this is why we can define both a type and a variable with the same name:

type Foo = { type: true }
var Foo = { value : true } // No error, no relation to Foo just have the same name in value space 

Class declarations and enums, since they span both spaces will 'use up' the name in both spaces and thus we can't define a variable or a type with the same name as a class declaration or enum (although we can do merging but that is a different concept)

In your specific case, Point is just a type, something we can use in type annotations, not something we can use in expressions that will need to have a runtime presence. In this case the type is useful as it allows the compiler to structurally check that the object literal is assignable to the Point type:

let p: Point = { x: 10, y: 15 }; // OK
let p: Point = { x: 10, y: 15, z: 10 }; // Error

If you want to create a class, you will need to do that with the class keyword, as that will create a runtime value that is not just a type:

class Point{
    constructor(public x: number, public y: number){}
}
let p = new Point(10,10)
2 of 5
32

You use a type (or in other cases an interface) for type annotations to indicate the structure of JavaScript objects:

type Point = {
  x: number, y: number
}

function doSomething(p: Point) {
}

let p: Point = { x: 10, y: 15 };
doSomething(p);

These type annotations are subject to structural typing, meaning that in this specific case you could drop the type annotation:

let p = { x: number, y: number };
doSomething(p);

A class is something entirely different, which provides you a more elegant alternative to JS prototype inheritance:

class Point {
    public x: number;
    public y: number;

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    public move(deltaX: number, deltaY: number) {
        this.x = this.x + deltaX;
        this.y = this.y + deltaY;
    }
}

let p = new Point(10, 15);
p.move(1, 2);

When you look at the generated JS code, you will quickly notice the difference:

The type declaration is dropped in the generated code.

The class definition is turned into a JS function with prototype inheritance

🌐
Medium
medium.com › @meetpujara02 › mastering-classes-in-typescript-a-dive-into-advanced-features-7bb5f219c714
Mastering Classes in TypeScript: A Dive into Advanced Features | by Meetpujara | Medium
December 11, 2024 - TypeScript’s class syntax extends JavaScript’s object-oriented programming capabilities, making it easier to structure and scale applications. The provided code explores various aspects of TypeScript classes, including encapsulation, inheritance, and advanced features like getters, setters, and modifiers.
🌐
TutorialsPoint
tutorialspoint.com › typescript › typescript_classes.htm
TypeScript - Classes
TypeScript supports object-oriented programming features like classes, interfaces, etc. A class in terms of OOP is a blueprint for creating objects. A class encapsulates data for the object.
🌐
Medium
thecodingdude.medium.com › classes-in-typescript-a4d20e9f8c33
Classes in Typescript. Certainly! Classes are an integral part… | by The Coding Dude | Medium
October 22, 2023 - Classes in Typescript Certainly! Classes are an integral part of TypeScript, and they enable you to create and manage objects with structured properties and methods. Here are some TypeScript code …
🌐
Graphite
graphite.com › guides › typescript-classes
TypeScript classes
This guide will explore the various aspects of TypeScript classes, including constructors, inheritance, abstract classes, static properties, and more
🌐
CodeSignal
codesignal.com › learn › courses › typescript-classes-and-objects-basics › lessons › exploring-typescript-classes-constructors-and-class-methods
TypeScript - Constructors and Class Methods
While TypeScript does not support multiple constructors, you can attain flexibility using default parameters, where a parameter is given a default value if no value or undefined is provided. Type annotations play a crucial role here, guaranteeing that parameters have defined types, even when default values are utilized. ... class Robot { name: string; color: string; constructor(name: string, color: string = 'grey') { this.name = name; this.color = color; } } const robotInstance = new Robot("Robbie", "red"); const robotInstance2 = new Robot("Bobby");
🌐
IONOS
ionos.com › digital guide › websites › web development › typescript classes
How to define and use TypeScript classes
July 12, 2024 - Classes are a key concept of Type­Script, which is a pro­gram­ming language based on JavaScript. Classes represent a struc­tured method for defining objects and for applying object-oriented pro­gram­ming (OOP).
🌐
Tutorial Teacher
tutorialsteacher.com › typescript › typescript-class
TypeScript Classes
When we instantiate a new object, the class constructor is called with the values passed and the member variables empCode and empName are initialized with these values. Just like object-oriented languages such as Java and C#, TypeScript classes can be extended to create new classes with inheritance, using the keyword extends.