From 75482fd15c49e2d6a423c35825771e130cca1ca0 Mon Sep 17 00:00:00 2001
From: Josh Vlk
Date: Fri, 18 Sep 2026 07:13:19 -0400
Subject: [PATCH 1/3] feat(dom): add safe element classifiers
---
src/dom-nodes/Element.res | 13 ++++++++++++-
src/html/HTMLElement.res | 13 +++++++++++++
src/html/HTMLInputElement.res | 13 +++++++++++++
src/window/Document.res | 13 ++++++++++++-
tests/DOMAPI/Classify__test.res | 33 +++++++++++++++++++++++++++++++++
tests/index.js | 1 +
6 files changed, 84 insertions(+), 2 deletions(-)
create mode 100644 tests/DOMAPI/Classify__test.res
diff --git a/src/dom-nodes/Element.res b/src/dom-nodes/Element.res
index 3ab0a09f..35c44a0e 100644
--- a/src/dom-nodes/Element.res
+++ b/src/dom-nodes/Element.res
@@ -506,4 +506,15 @@ Returns true if qualifiedName is now present, and false otherwise.
include Impl({type t = t})
-let isInstanceOf = (_: 't): bool => %raw(`param instanceof Element`)
+let isInstanceOf = (_: 'value): bool =>
+ %raw(`typeof globalThis.Element === "function" && param instanceof globalThis.Element`)
+
+/**
+Returns the value as an Element when it is an Element in the current realm, and None otherwise.
+*/
+let classify = (value: 'value): option =>
+ if value->isInstanceOf {
+ Some(Obj.magic(value))
+ } else {
+ None
+ }
diff --git a/src/html/HTMLElement.res b/src/html/HTMLElement.res
index 25c513b7..6b87a9f8 100644
--- a/src/html/HTMLElement.res
+++ b/src/html/HTMLElement.res
@@ -106,3 +106,16 @@ rather than relying on coercion.
}
include Impl({type t = t})
+
+let isInstanceOf = (_: 'value): bool =>
+ %raw(`typeof globalThis.HTMLElement === "function" && param instanceof globalThis.HTMLElement`)
+
+/**
+Returns the value as an HTMLElement when it is an HTMLElement in the current realm, and None otherwise.
+*/
+let classify = (value: 'value): option =>
+ if value->isInstanceOf {
+ Some(Obj.magic(value))
+ } else {
+ None
+ }
diff --git a/src/html/HTMLInputElement.res b/src/html/HTMLInputElement.res
index d90e5fbd..32e0e6a2 100644
--- a/src/html/HTMLInputElement.res
+++ b/src/html/HTMLInputElement.res
@@ -230,6 +230,19 @@ type t = {
include HTMLElement.Impl({type t = t})
+let isInstanceOf = (_: 'value): bool =>
+ %raw(`typeof globalThis.HTMLInputElement === "function" && param instanceof globalThis.HTMLInputElement`)
+
+/**
+Returns the value as an HTMLInputElement when it is an HTMLInputElement in the current realm, and None otherwise.
+*/
+let classify = (value: 'value): option =>
+ if value->isInstanceOf {
+ Some(Obj.magic(value))
+ } else {
+ None
+ }
+
/**
Increments a range input control's value by the value given by the Step attribute. If the optional parameter is used, will increment the input control's value by that value.
@param n Value to increment the value by.
diff --git a/src/window/Document.res b/src/window/Document.res
index b38bcab3..4908a402 100644
--- a/src/window/Document.res
+++ b/src/window/Document.res
@@ -460,7 +460,18 @@ external hasStorageAccess: DOM.document => promise = "hasStorageAccess"
@send
external requestStorageAccess: DOM.document => promise = "requestStorageAccess"
-let isInstanceOf = (_: 't): bool => %raw(`param instanceof Document`)
+let isInstanceOf = (_: 'value): bool =>
+ %raw(`typeof globalThis.Document === "function" && param instanceof globalThis.Document`)
+
+/**
+Returns the value as a Document when it is a Document in the current realm, and None otherwise.
+*/
+let classify = (value: 'value): option =>
+ if value->isInstanceOf {
+ Some(Obj.magic(value))
+ } else {
+ None
+ }
/**
Returns the Location associated with this document, which provides information about the current URL and methods for navigating to another URL.
diff --git a/tests/DOMAPI/Classify__test.res b/tests/DOMAPI/Classify__test.res
new file mode 100644
index 00000000..296edcc9
--- /dev/null
+++ b/tests/DOMAPI/Classify__test.res
@@ -0,0 +1,33 @@
+%%raw(`
+globalThis.Element = class Element {}
+globalThis.Document = class Document {}
+globalThis.HTMLElement = class HTMLElement extends globalThis.Element {}
+globalThis.HTMLInputElement = class HTMLInputElement extends globalThis.HTMLElement {
+ checkValidity() {
+ return true
+ }
+}
+`)
+
+let element: unknown = %raw(`new globalThis.Element()`)
+let document: unknown = %raw(`new globalThis.Document()`)
+let htmlElement: unknown = %raw(`new globalThis.HTMLElement()`)
+let input: unknown = %raw(`new globalThis.HTMLInputElement()`)
+
+assert(element->Element.classify->Option.isSome)
+assert(document->Document.classify->Option.isSome)
+assert(htmlElement->HTMLElement.classify->Option.isSome)
+assert(input->HTMLInputElement.classify->Option.isSome)
+
+assert(input->Element.classify->Option.isSome)
+assert(input->HTMLElement.classify->Option.isSome)
+assert(document->Element.classify->Option.isNone)
+assert(htmlElement->HTMLInputElement.classify->Option.isNone)
+
+input
+->HTMLInputElement.classify
+->Option.forEach(input => assert(input->HTMLInputElement.checkValidity))
+
+%%raw(`delete globalThis.HTMLInputElement`)
+
+assert(input->HTMLInputElement.classify->Option.isNone)
diff --git a/tests/index.js b/tests/index.js
index 9d73db6a..c989c737 100644
--- a/tests/index.js
+++ b/tests/index.js
@@ -12,6 +12,7 @@ const rescriptConfig = JSON.parse(fs.readFileSync(path.join(repoRoot, "rescript.
const compiledSuffix = rescriptConfig.suffix ?? ".js";
const runtimeTests = [
+ "DOMAPI/Classify__test.res",
"FetchAPI/Headers__test.res",
"FetchAPI/Request__test.res",
"FetchAPI/Response__test.res",
From 83a0ce6969626daa20f9f62eda329692b39a3645 Mon Sep 17 00:00:00 2001
From: Josh Vlk
Date: Fri, 18 Sep 2026 07:20:59 -0400
Subject: [PATCH 2/3] docs(dom): explain element classifiers
---
docs/content/docs/api-surface.mdx | 27 +++++++++++++++++++++++++++
docs/content/docs/philosophy.mdx | 17 ++++++++++++++++-
src/dom-nodes/Element.res | 23 ++++++++++++++++++++++-
src/html/HTMLElement.res | 23 ++++++++++++++++++++++-
src/html/HTMLInputElement.res | 23 ++++++++++++++++++++++-
src/window/Document.res | 20 +++++++++++++++++++-
6 files changed, 128 insertions(+), 5 deletions(-)
diff --git a/docs/content/docs/api-surface.mdx b/docs/content/docs/api-surface.mdx
index cc80575a..0cbcb022 100644
--- a/docs/content/docs/api-surface.mdx
+++ b/docs/content/docs/api-surface.mdx
@@ -300,6 +300,33 @@ let element = document->WebAPI.Document.createElement("div")
let node = element->WebAPI.Element.asNode
```
+### Classifying DOM values
+
+Event libraries and browser APIs sometimes return a type that is broader than the value present at
+runtime. A React form event target, for example, does not tell ReScript whether the target is an
+input, select, or another element. Calling an input-specific API then requires narrowing that value.
+
+Use `classify` instead of repeating an `instanceof` binding and an unchecked `Obj.magic` cast. The
+classifier performs the runtime check and returns the correctly typed value as an `option`.
+
+```ReScript
+let readInputValue = target =>
+ switch target->WebAPI.HTMLInputElement.classify {
+ | Some(input) => Some(input.value)
+ | None => None
+ }
+```
+
+The classifier parameter is polymorphic, so a library-owned target can be passed directly without an
+identity cast to a WebAPI type first. `Element.classify`, `Document.classify`,
+`HTMLElement.classify`, and `HTMLInputElement.classify` follow the same contract. Their matching
+`isInstanceOf` functions are available when only a boolean check is needed.
+
+Classification uses the corresponding constructor from `globalThis`. It returns `None` rather than
+raising when that constructor is unavailable in a server or worker environment. Like JavaScript
+`instanceof`, it only recognizes values created in the current realm; values from another window or
+iframe may not match. Enable the `WebAPI.HTML` feature when using the HTML-specific classifiers.
+
## Visual Viewport
`Window.visualViewport` returns a nullable `WebAPI.VisualViewport.t`.
diff --git a/docs/content/docs/philosophy.mdx b/docs/content/docs/philosophy.mdx
index 2b52fa65..32a0f623 100644
--- a/docs/content/docs/philosophy.mdx
+++ b/docs/content/docs/philosophy.mdx
@@ -49,4 +49,19 @@ let element: WebAPI.Element.t = document->WebAPI.Document.createElement("div")
let node: WebAPI.Node.t = element->WebAPI.Element.asNode
```
-Any other conversions should be treated as unsafe casts and used with caution, because the type system cannot guarantee they are valid at runtime.
+Converting in the other direction requires proving the runtime type. Broad DOM values commonly come
+from event libraries, selector APIs, or JavaScript bindings that cannot describe the concrete
+interface statically. Use the destination module's `classify` function for that downcast instead of
+calling `Obj.magic` directly.
+
+```ReScript
+switch target->WebAPI.HTMLInputElement.classify {
+| Some(input) => Some(input.value)
+| None => None
+}
+```
+
+Classifiers return an `option` because a runtime value may not implement the requested interface.
+They also return `None` when the browser constructor is unavailable. The checked cast remains inside
+the binding, so application code handles absence explicitly and receives the concrete public
+interface type only after a successful check.
diff --git a/src/dom-nodes/Element.res b/src/dom-nodes/Element.res
index 35c44a0e..768e384a 100644
--- a/src/dom-nodes/Element.res
+++ b/src/dom-nodes/Element.res
@@ -506,11 +506,32 @@ Returns true if qualifiedName is now present, and false otherwise.
include Impl({type t = t})
+/**
+`isInstanceOf(value)`
+
+Returns whether `value` is an `Element` created in the current JavaScript realm.
+
+This is a runtime check. It returns `false` when `globalThis.Element` is unavailable, such as in
+some server or worker environments.
+*/
let isInstanceOf = (_: 'value): bool =>
%raw(`typeof globalThis.Element === "function" && param instanceof globalThis.Element`)
/**
-Returns the value as an Element when it is an Element in the current realm, and None otherwise.
+`classify(value)`
+
+Safely narrows a value from a broad DOM or library type to `Element.t`.
+
+Use this when an event target, JavaScript binding, or union-like API cannot express its concrete DOM
+type statically. Returns `Some(element)` when the value is an `Element` in the current realm, and
+`None` when it is not or when the `Element` constructor is unavailable.
+
+```res
+switch value->Element.classify {
+| Some(element) => element->Element.hasAttribute("data-ready")
+| None => false
+}
+```
*/
let classify = (value: 'value): option =>
if value->isInstanceOf {
diff --git a/src/html/HTMLElement.res b/src/html/HTMLElement.res
index 6b87a9f8..4f436620 100644
--- a/src/html/HTMLElement.res
+++ b/src/html/HTMLElement.res
@@ -107,11 +107,32 @@ rather than relying on coercion.
include Impl({type t = t})
+/**
+`isInstanceOf(value)`
+
+Returns whether `value` is an `HTMLElement` created in the current JavaScript realm.
+
+This is a runtime check. It returns `false` when `globalThis.HTMLElement` is unavailable, such as in
+some server or worker environments.
+*/
let isInstanceOf = (_: 'value): bool =>
%raw(`typeof globalThis.HTMLElement === "function" && param instanceof globalThis.HTMLElement`)
/**
-Returns the value as an HTMLElement when it is an HTMLElement in the current realm, and None otherwise.
+`classify(value)`
+
+Safely narrows a value from a broad DOM or library type to `HTMLElement.t`.
+
+Use this when an event target or element-returning API does not distinguish HTML elements from SVG
+or other element kinds. Returns `Some(element)` for an `HTMLElement` in the current realm, and
+`None` when the value is not an HTML element or when the `HTMLElement` constructor is unavailable.
+
+```res
+switch value->HTMLElement.classify {
+| Some(element) => element->HTMLElement.focus
+| None => ()
+}
+```
*/
let classify = (value: 'value): option =>
if value->isInstanceOf {
diff --git a/src/html/HTMLInputElement.res b/src/html/HTMLInputElement.res
index 32e0e6a2..bee1478a 100644
--- a/src/html/HTMLInputElement.res
+++ b/src/html/HTMLInputElement.res
@@ -230,11 +230,32 @@ type t = {
include HTMLElement.Impl({type t = t})
+/**
+`isInstanceOf(value)`
+
+Returns whether `value` is an `HTMLInputElement` created in the current JavaScript realm.
+
+This is a runtime check. It returns `false` when `globalThis.HTMLInputElement` is unavailable, such
+as in some server or worker environments.
+*/
let isInstanceOf = (_: 'value): bool =>
%raw(`typeof globalThis.HTMLInputElement === "function" && param instanceof globalThis.HTMLInputElement`)
/**
-Returns the value as an HTMLInputElement when it is an HTMLInputElement in the current realm, and None otherwise.
+`classify(value)`
+
+Safely narrows a value from a broad event target or element type to `HTMLInputElement.t`.
+
+Use this for values such as React form event targets, which are typed more broadly than the input
+element that emitted the event. Returns `Some(input)` for an `HTMLInputElement` in the current realm,
+and `None` when the value is not an input or when the `HTMLInputElement` constructor is unavailable.
+
+```res
+switch target->HTMLInputElement.classify {
+| Some(input) => Some(input.value)
+| None => None
+}
+```
*/
let classify = (value: 'value): option =>
if value->isInstanceOf {
diff --git a/src/window/Document.res b/src/window/Document.res
index 4908a402..c6b0315b 100644
--- a/src/window/Document.res
+++ b/src/window/Document.res
@@ -460,11 +460,29 @@ external hasStorageAccess: DOM.document => promise = "hasStorageAccess"
@send
external requestStorageAccess: DOM.document => promise = "requestStorageAccess"
+/**
+`isInstanceOf(value)`
+
+Returns whether `value` is a `Document` created in the current JavaScript realm.
+
+This is a runtime check. It returns `false` when `globalThis.Document` is unavailable, such as in
+some server or worker environments.
+*/
let isInstanceOf = (_: 'value): bool =>
%raw(`typeof globalThis.Document === "function" && param instanceof globalThis.Document`)
/**
-Returns the value as a Document when it is a Document in the current realm, and None otherwise.
+`classify(value)`
+
+Safely narrows a value from a broad DOM or library type to `DOM.document`.
+
+Use this when a JavaScript binding or union-like API can contain a document but cannot express that
+concrete type statically. Returns `Some(document)` when the value is a `Document` in the current
+realm, and `None` when it is not or when the `Document` constructor is unavailable.
+
+```res
+let document = value->Document.classify
+```
*/
let classify = (value: 'value): option =>
if value->isInstanceOf {
From 670bba77b490ae3cde9b56c3ccee10c1b93be919 Mon Sep 17 00:00:00 2001
From: Josh Vlk
Date: Fri, 18 Sep 2026 09:53:35 -0400
Subject: [PATCH 3/3] fix(dom): promote example helpers to public APIs
---
docs/content/docs/api-surface.mdx | 8 ++--
docs/pages/examples.astro | 4 +-
src/canvas/CanvasRenderingContext2D.res | 25 ++++++++++
src/canvas/HTMLCanvasElement.res | 34 +++++++++++++
src/html/HTMLImageElement.res | 34 +++++++++++++
tests/DOMAPI/Classify__test.res | 15 +++++-
tests/DOMAPI/HTMLCanvasElement__test.res | 61 ++++++++++++------------
tests/DOMAPI/HTMLInputElement__test.res | 9 ++--
8 files changed, 148 insertions(+), 42 deletions(-)
diff --git a/docs/content/docs/api-surface.mdx b/docs/content/docs/api-surface.mdx
index 0cbcb022..798bc3ec 100644
--- a/docs/content/docs/api-surface.mdx
+++ b/docs/content/docs/api-surface.mdx
@@ -319,13 +319,15 @@ let readInputValue = target =>
The classifier parameter is polymorphic, so a library-owned target can be passed directly without an
identity cast to a WebAPI type first. `Element.classify`, `Document.classify`,
-`HTMLElement.classify`, and `HTMLInputElement.classify` follow the same contract. Their matching
-`isInstanceOf` functions are available when only a boolean check is needed.
+`HTMLElement.classify`, `HTMLInputElement.classify`, `HTMLImageElement.classify`, and
+`HTMLCanvasElement.classify` follow the same contract. Their matching `isInstanceOf` functions are
+available when only a boolean check is needed.
Classification uses the corresponding constructor from `globalThis`. It returns `None` rather than
raising when that constructor is unavailable in a server or worker environment. Like JavaScript
`instanceof`, it only recognizes values created in the current realm; values from another window or
-iframe may not match. Enable the `WebAPI.HTML` feature when using the HTML-specific classifiers.
+iframe may not match. Enable the `WebAPI.HTML` feature for HTML-specific classifiers and
+`WebAPI.Canvas` for `HTMLCanvasElement.classify`.
## Visual Viewport
diff --git a/docs/pages/examples.astro b/docs/pages/examples.astro
index 871e59cc..53ec1320 100644
--- a/docs/pages/examples.astro
+++ b/docs/pages/examples.astro
@@ -19,8 +19,8 @@ const headings = testFiles.map(({ name }) => ({
The best example of usage of these bindings are the tests.
- These typically contain tweaks made to the generated bindings to make them more
- ergonomic.
+ These exercise the public bindings directly. Reusable Web API bindings and conversion
+ helpers belong in the library rather than in individual examples.
{
testFiles.map(({ source, output, name }) => {
diff --git a/src/canvas/CanvasRenderingContext2D.res b/src/canvas/CanvasRenderingContext2D.res
index aa649758..0bde4367 100644
--- a/src/canvas/CanvasRenderingContext2D.res
+++ b/src/canvas/CanvasRenderingContext2D.res
@@ -1,3 +1,28 @@
+/**
+[Read more on MDN](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/fillStyle)
+*/
+@get
+external getFillStyle: DOM.canvasRenderingContext2D => CanvasTypes.fillStyle = "fillStyle"
+
+/**
+[Read more on MDN](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/fillStyle)
+*/
+@set
+external setFillStyle: (DOM.canvasRenderingContext2D, CanvasTypes.fillStyle) => unit = "fillStyle"
+
+/**
+[Read more on MDN](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/font)
+*/
+@set
+external setFont: (DOM.canvasRenderingContext2D, string) => unit = "font"
+
+/**
+[Read more on MDN](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/textBaseline)
+*/
+@set
+external setTextBaseline: (DOM.canvasRenderingContext2D, CanvasTypes.canvasTextBaseline) => unit =
+ "textBaseline"
+
/**
[Read more on MDN](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/save)
*/
diff --git a/src/canvas/HTMLCanvasElement.res b/src/canvas/HTMLCanvasElement.res
index 9288e87b..885e3f40 100644
--- a/src/canvas/HTMLCanvasElement.res
+++ b/src/canvas/HTMLCanvasElement.res
@@ -19,6 +19,40 @@ type t = {
include HTMLElement.Impl({type t = t})
+/**
+`isInstanceOf(value)`
+
+Returns whether `value` is an `HTMLCanvasElement` created in the current JavaScript realm.
+
+This is a runtime check. It returns `false` when `globalThis.HTMLCanvasElement` is unavailable, such
+as in some server or worker environments.
+*/
+let isInstanceOf = (_: 'value): bool =>
+ %raw(`typeof globalThis.HTMLCanvasElement === "function" && param instanceof globalThis.HTMLCanvasElement`)
+
+/**
+`classify(value)`
+
+Safely narrows a value from a broad DOM element type to `HTMLCanvasElement.t`.
+
+Use this for values returned by APIs such as `Document.getElementById`. Returns `Some(canvas)` for
+an `HTMLCanvasElement` in the current realm, and `None` when the value is not a canvas or when the
+`HTMLCanvasElement` constructor is unavailable.
+
+```res
+switch element->HTMLCanvasElement.classify {
+| Some(canvas) => canvas->HTMLCanvasElement.getContext2D
+| None => Null
+}
+```
+*/
+let classify = (value: 'value): option =>
+ if value->isInstanceOf {
+ Some(Obj.magic(value))
+ } else {
+ None
+ }
+
/**
Returns an object that provides methods and properties for drawing and manipulating images and graphics on a canvas element in a document. A context object includes information about colors, line widths, fonts, and other graphic parameters that can be drawn on a canvas.
Creates a CanvasRenderingContext2D object representing a two-dimensional rendering context.
diff --git a/src/html/HTMLImageElement.res b/src/html/HTMLImageElement.res
index 859a0c54..7e08a6bf 100644
--- a/src/html/HTMLImageElement.res
+++ b/src/html/HTMLImageElement.res
@@ -342,6 +342,40 @@ type t = {
include HTMLElement.Impl({type t = t})
+/**
+`isInstanceOf(value)`
+
+Returns whether `value` is an `HTMLImageElement` created in the current JavaScript realm.
+
+This is a runtime check. It returns `false` when `globalThis.HTMLImageElement` is unavailable, such
+as in some server or worker environments.
+*/
+let isInstanceOf = (_: 'value): bool =>
+ %raw(`typeof globalThis.HTMLImageElement === "function" && param instanceof globalThis.HTMLImageElement`)
+
+/**
+`classify(value)`
+
+Safely narrows a value from a broad DOM element type to `HTMLImageElement.t`.
+
+Use this for values returned by element-creating or selector APIs. Returns `Some(image)` for an
+`HTMLImageElement` in the current realm, and `None` when the value is not an image or when the
+`HTMLImageElement` constructor is unavailable.
+
+```res
+switch element->HTMLImageElement.classify {
+| Some(image) => Some(image.src)
+| None => None
+}
+```
+*/
+let classify = (value: 'value): option =>
+ if value->isInstanceOf {
+ Some(Obj.magic(value))
+ } else {
+ None
+ }
+
/**
[Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLImageElement/decode)
*/
diff --git a/tests/DOMAPI/Classify__test.res b/tests/DOMAPI/Classify__test.res
index 296edcc9..3a4799ad 100644
--- a/tests/DOMAPI/Classify__test.res
+++ b/tests/DOMAPI/Classify__test.res
@@ -2,6 +2,8 @@
globalThis.Element = class Element {}
globalThis.Document = class Document {}
globalThis.HTMLElement = class HTMLElement extends globalThis.Element {}
+globalThis.HTMLCanvasElement = class HTMLCanvasElement extends globalThis.HTMLElement {}
+globalThis.HTMLImageElement = class HTMLImageElement extends globalThis.HTMLElement {}
globalThis.HTMLInputElement = class HTMLInputElement extends globalThis.HTMLElement {
checkValidity() {
return true
@@ -12,22 +14,33 @@ globalThis.HTMLInputElement = class HTMLInputElement extends globalThis.HTMLElem
let element: unknown = %raw(`new globalThis.Element()`)
let document: unknown = %raw(`new globalThis.Document()`)
let htmlElement: unknown = %raw(`new globalThis.HTMLElement()`)
+let canvas: unknown = %raw(`new globalThis.HTMLCanvasElement()`)
+let image: unknown = %raw(`new globalThis.HTMLImageElement()`)
let input: unknown = %raw(`new globalThis.HTMLInputElement()`)
assert(element->Element.classify->Option.isSome)
assert(document->Document.classify->Option.isSome)
assert(htmlElement->HTMLElement.classify->Option.isSome)
+assert(canvas->HTMLCanvasElement.classify->Option.isSome)
+assert(image->HTMLImageElement.classify->Option.isSome)
assert(input->HTMLInputElement.classify->Option.isSome)
assert(input->Element.classify->Option.isSome)
assert(input->HTMLElement.classify->Option.isSome)
assert(document->Element.classify->Option.isNone)
+assert(image->HTMLCanvasElement.classify->Option.isNone)
assert(htmlElement->HTMLInputElement.classify->Option.isNone)
input
->HTMLInputElement.classify
->Option.forEach(input => assert(input->HTMLInputElement.checkValidity))
-%%raw(`delete globalThis.HTMLInputElement`)
+%%raw(`
+delete globalThis.HTMLCanvasElement
+delete globalThis.HTMLImageElement
+delete globalThis.HTMLInputElement
+`)
+assert(canvas->HTMLCanvasElement.classify->Option.isNone)
+assert(image->HTMLImageElement.classify->Option.isNone)
assert(input->HTMLInputElement.classify->Option.isNone)
diff --git a/tests/DOMAPI/HTMLCanvasElement__test.res b/tests/DOMAPI/HTMLCanvasElement__test.res
index 4ac1156a..c074557b 100644
--- a/tests/DOMAPI/HTMLCanvasElement__test.res
+++ b/tests/DOMAPI/HTMLCanvasElement__test.res
@@ -1,37 +1,36 @@
-external toHTMLCanvasElement: null => HTMLCanvasElement.t = "%identity"
-@set
-external setFillStyle: (DOM.canvasRenderingContext2D, CanvasTypes.fillStyle) => unit = "fillStyle"
-@get
-external getFillStyle: DOM.canvasRenderingContext2D => CanvasTypes.fillStyle = "fillStyle"
-@set
-external setFont: (DOM.canvasRenderingContext2D, string) => unit = "font"
-@set
-external setTextBaseline: (DOM.canvasRenderingContext2D, CanvasTypes.canvasTextBaseline) => unit =
- "textBaseline"
+let draw = (ctx: DOM.canvasRenderingContext2D) => {
+ ctx->CanvasRenderingContext2D.setFillStyle(FillStyle.fromString("red"))
+ ctx->CanvasRenderingContext2D.fillRect(~x=50., ~y=50., ~w=200., ~h=200.)
-let myCanvas: HTMLCanvasElement.t =
- DomGlobal.document->Document.getElementById("myCanvas")->toHTMLCanvasElement
-let ctx = myCanvas->HTMLCanvasElement.getContext2D->Null.getOrThrow
+ ctx->CanvasRenderingContext2D.setFillStyle(FillStyle.fromString("black"))
+ ctx->CanvasRenderingContext2D.setFont("2px Tahoma")
+ ctx->CanvasRenderingContext2D.setTextBaseline(CanvasTypes.Top)
+ ctx->CanvasRenderingContext2D.fillText(~text="MY TEXT", ~x=60., ~y=60.)
-ctx->setFillStyle(FillStyle.fromString("red"))
-ctx->CanvasRenderingContext2D.fillRect(~x=50., ~y=50., ~w=200., ~h=200.)
+ switch ctx->CanvasRenderingContext2D.getFillStyle->FillStyle.decode {
+ | FillStyle.String(color) => Console.log(`Color: ${color}`)
+ | FillStyle.CanvasGradient(_) => Console.log("CanvasGradient")
+ | FillStyle.CanvasPattern(_) => Console.log("CanvasPattern")
+ }
-ctx->setFillStyle(FillStyle.fromString("black"))
-ctx->setFont("2px Tahoma")
-ctx->setTextBaseline(CanvasTypes.Top)
-ctx->CanvasRenderingContext2D.fillText(~text="MY TEXT", ~x=60., ~y=60.)
-
-switch ctx->getFillStyle->FillStyle.decode {
-| FillStyle.String(color) => Console.log(`Color: ${color}`)
-| FillStyle.CanvasGradient(_) => Console.log("CanvasGradient")
-| FillStyle.CanvasPattern(_) => Console.log("CanvasPattern")
+ DomGlobal.document
+ ->Document.createElement("img")
+ ->HTMLImageElement.classify
+ ->Option.forEach(image =>
+ ctx->CanvasRenderingContext2D.drawImageWithDimensions(
+ ~image,
+ ~dx=0.,
+ ~dy=0.,
+ ~dw=200.,
+ ~dh=200.,
+ )
+ )
}
-let img: HTMLImageElement.t = DomGlobal.document->Document.createElement("img")->Obj.magic
-ctx->CanvasRenderingContext2D.drawImageWithDimensions(
- ~image=img,
- ~dx=0.,
- ~dy=0.,
- ~dw=200.,
- ~dh=200.,
+DomGlobal.document
+->Document.getElementById("myCanvas")
+->Null.toOption
+->Option.flatMap(element => element->HTMLCanvasElement.classify)
+->Option.forEach(canvas =>
+ canvas->HTMLCanvasElement.getContext2D->Null.toOption->Option.forEach(draw)
)
diff --git a/tests/DOMAPI/HTMLInputElement__test.res b/tests/DOMAPI/HTMLInputElement__test.res
index 41e76ce0..35758f5e 100644
--- a/tests/DOMAPI/HTMLInputElement__test.res
+++ b/tests/DOMAPI/HTMLInputElement__test.res
@@ -1,5 +1,4 @@
-external toHTMLInputElement: DOMTree.element => HTMLInputElement.t = "%identity"
-
-let input: HTMLInputElement.t =
- DomGlobal.document->Document.createElement("input")->toHTMLInputElement
-let value = input.value
+let value = switch DomGlobal.document->Document.createElement("input")->HTMLInputElement.classify {
+| Some(input) => Some(input.value)
+| None => None
+}