From 8e224daaccef198b7cedb8a06a3e0955cbea5333 Mon Sep 17 00:00:00 2001 From: Florian Hammerschmidt Date: Wed, 21 Oct 2026 15:21:00 +0200 Subject: [PATCH] docs: port class inheritance guide to v13 Forward-port the JavaScript inheritance guide and type-alias cross-link from master PR #1462. Source commit: 1f47ac650a63b4133878ef1eeab45de8f27c7644 --- .../docs/manual/bind-to-js-object.mdx | 75 +++++++++++++++++++ apps/docs/markdown-pages/docs/manual/type.mdx | 2 + 2 files changed, 77 insertions(+) diff --git a/apps/docs/markdown-pages/docs/manual/bind-to-js-object.mdx b/apps/docs/markdown-pages/docs/manual/bind-to-js-object.mdx index 9bbabe767..ee1255483 100644 --- a/apps/docs/markdown-pages/docs/manual/bind-to-js-object.mdx +++ b/apps/docs/markdown-pages/docs/manual/bind-to-js-object.mdx @@ -216,3 +216,78 @@ export { myBook }; ``` + +### Represent Class Inheritance + +ReScript has no class declarations, but bindings can model existing JavaScript inheritance using **private type abbreviations**. Suppose `animals.js` exports the hierarchy `Animal → Mammal → Dog`: + +```js +// animals.js +export class Animal { + constructor(name) { + this.name = name; + } + + getName() { + return this.name; + } +} + +export class Mammal extends Animal {} + +export class Dog extends Mammal { + bark() { + return "Woof!"; + } +} +``` + +Declare the base type and its methods in `Animal.res`: + +```res file=Animal.res +type t + +@send external getName: t => string = "getName" +``` + +In `Mammal.res`, relate the subclass to its parent: + +```res file=Mammal.res +type t = private Animal.t +``` + +Repeat for `Dog.res`, adding its constructor and methods: + +```res file=Dog.res +type t = private Mammal.t + +@new @module("./animals.js") external make: string => t = "Dog" +@send external bark: t => string = "bark" +``` + +Use `:>` to **coerce** a subclass instance to a parent type: + +```res +let dog = Dog.make("Rex") +let mammal = (dog :> Mammal.t) +let animal = (dog :> Animal.t) + +let name = animal->Animal.getName +let sound = dog->Dog.bark +``` + +Coercion works through the hierarchy: `Dog.t` can be coerced directly to `Animal.t`. It changes only the type; all three variables refer to the same JavaScript object. + +Unlike a regular [type alias](./type.mdx#type-alias), `private` keeps the types distinct. Coercion requires `:>`; a `:` annotation alone is not enough. The reverse conversion from `Animal.t` to `Dog.t` is rejected, so keep the original `Dog.t` value if you need dog-specific methods. + +Private abbreviations describe an existing inheritance relationship; they do not create inheritance or check instances at runtime. + +#### Interfaces + +If you add a [`.resi` interface](./module.mdx#signatures), expose the same relationship. For example, `Mammal.resi` should contain: + +```res file=Mammal.resi +type t = private Animal.t +``` + +Exposing only `type t` would hide the relationship and prevent callers from coercing `Mammal.t` to `Animal.t`. diff --git a/apps/docs/markdown-pages/docs/manual/type.mdx b/apps/docs/markdown-pages/docs/manual/type.mdx index b4dbc50fb..205500306 100644 --- a/apps/docs/markdown-pages/docs/manual/type.mdx +++ b/apps/docs/markdown-pages/docs/manual/type.mdx @@ -113,6 +113,8 @@ export { x }; +For JavaScript bindings, [private type abbreviations](./bind-to-js-object.mdx#represent-class-inheritance) can preserve distinct types while allowing explicit coercion to a parent type, as needed for class inheritance. + ## Type Parameter (Aka Generic) Types can accept parameters, akin to generics in other languages. The parameters' names **need** to start with `'`.