TypeScript's parameter properties look like Kotlin's primary constructor, but the runtime underneath is JavaScript. Here is the same Book, compiled with TypeScript 7.0.2 (tsc --strict) and run with Node 22:
class Book {
constructor(public readonly id: number, public readonly title: string,
public readonly price: number) {}
}
const a = new Book(1, "The Quiet Harbor", 14.99);
const b = new Book(1, "The Quiet Harbor", 14.99);
console.log(a === b, JSON.stringify(a) === JSON.stringify(b), `${a}`);
const sale = { ...a, price: 11.99 }; // nearest thing to copy()
const fake: Book = { id: 9, title: "Not a Book", price: 0 }; // structural typing
console.log(sale instanceof Book, fake instanceof Book);Output
false true [object Object] false false
JavaScript has no value equality for objects, so you compare serialized forms or hand-write equals(). The spread copy is a plain object that has lost its prototype, and tsc accepts fake as a Book because TypeScript types are structural: any object of the right shape fits. Kotlin types are nominal, so only a real Book is a Book. TypeScript's private also vanishes at run time; only #field is enforced there.