Scala
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Scala
-
Section 4: Functional Scala
-
Section 5: Collections in Depth
-
Section 6: Type System
-
Section 7: Concurrency and Ecosystem
-
Section 8: Practical Projects
-
Section 9: Interview Practice
-
Section 10: Data Structures and Algorithms in Scala
-
Section 11: More Practice Exercises
-
Section 12: Advanced Functional Patterns
-
Section 13: More Ecosystem
-
Section 14: Scala Collections Library Deep Dive
-
Section 15: Scala Standard Library Deep Dive
-
Section 16: Akka Ecosystem Deep Dive
-
Section 17: Cats and Cats Effect Deep Dive
-
Section 18: Play Framework Deep Dive
-
Section 19: Apache Spark with Scala Deep Dive
-
Section 20: Scala Build Tools Deep Dive
-
Section 21: Scala 3 Specific Features
-
90: Union and Intersection Types
-
Section 22: Scala Testing Deep Dive
-
Section 23: Functional Domain Modeling
-
Section 24: More Data Structures and Algorithms in Scala
-
Section 25: Scala for Data Engineering
-
Section 26: More Practical Projects
-
Section 27: More Interview and Review
-
Section 28: ZIO Ecosystem Deep Dive
-
Section 29: Scala for Machine Learning
-
Section 30: Scala Microservices Architecture
-
Section 31: Scala Type System Deep Dive
-
Section 32: More Practice and Drills
-
Section 33: Scala Performance Deep Dive
-
Section 34: Scala Ecosystem Tooling
-
Section 35: Scala for Reactive Systems
-
Section 36: More Real-World Case Studies
-
Section 37: Scala for Financial Systems
-
Section 38: Scala GraphQL and gRPC
-
Section 39: More Final Projects
-
Section 40: More Interview and Final Review
-
Section 41: Scala for Streaming Data
-
Section 42: Scala Security Practices
-
Section 43: More Language Deep Dive
-
Section 44: Scala Command-Line Tools
-
Section 45: Scala Documentation and Style
-
Section 46: Scala Dependency Management
-
Section 47: More Practical Backend Patterns
-
Section 48: Scala for Event-Driven Architecture
-
Section 49: More Practice Drills Round 2
-
Section 50: Scala Compiler Deep Dive
-
Section 51: Scala for Web Frontends
-
Section 52: More Data Engineering Practice
-
Section 53: Scala Observability
-
Section 54: More Advanced Practice Projects
-
Section 55: Scala for Legacy Java Integration
-
Section 56: More Testing Practice
-
Section 57: Final Mastery Review
-
Section 58: Scala History and Ecosystem Context
-
Section 59: More Concurrency Patterns
-
Section 60: Scala for Configuration Management
-
Section 61: More Domain Modeling Practice
202: Interfacing Scala.js with JavaScript Libraries
I've spent more hours than I care to admit staring at TypeError: confetti is not a function in the browser console. It usually happens when you're feeling confident, you've found a great NPM package, and you've written a clean Scala facade for it—only for the runtime to tell you that your facade is a lie.
The "Not a Function" Nightmare
Let's say you want to add a bit of polish to your app using canvas-confetti. It's a simple library: you call a function, and confetti flies. You'd probably start by writing a facade like this:
import scala.scalajs.js import scala.scalajs.js.annotation._ @JSImport("canvas-confetti", "confetti") external val confetti: js.JSFunction1[js.Any, js.Any] = confettiYou compile, you run, and you call
confetti(js.Dynamic.literal()). Immediately, the console screams thatconfettiisundefined. You check the NPM package, and yes, the function is there. You check your import string, and it matches the package name. So why is it broken?The issue is a fundamental mismatch between how JavaScript handles Named Exports versus Default Exports. When you write
@JSImport("canvas-confetti", "confetti"), you are telling Scala.js to look for a specific export namedconfettiinside the module. Butcanvas-confettiusesexport default. In the eyes of the JavaScript module system, the "name" of that export isn'tconfetti—it'sdefault.Mapping Default Exports Correctly
To fix this, you have to be explicit about the default export. In Scala.js, the default export is literally named
"default"in the import annotation. Here is the correction:@JSImport("canvas-confetti", "default") external val confetti: js.JSFunction1[js.Any, js.Any] = confettiNow, when the JS engine loads the module, Scala.js grabs the
defaultproperty of the module object, which happens to be the confetti function we want. If you're ever unsure whether a library uses a default or named export, I highly recommend peekng at thepackage.jsonor the source code on GitHub. If you seemodule.exports = ...orexport default ..., you need to use"default".Moving Beyond js.Any with @JSNative
While
js.Anygets the code running, it's a bit lazy. We're using Scala because we like types, so passingjs.Anyinto a confetti configuration object feels wrong. I don't want to rely onjs.Dynamic.literal()every time I call a function; it's error-prone and lacks autocomplete.The "professional" way to handle JS options is to define a
@JSNativetrait. This allows you to define the shape of the JavaScript object you expect to pass, providing type safety on the Scala side without adding any runtime overhead.@JSNative trait ConfettiOptions extends js.Object { val particleCount: js.Int val spread: js.Int val origin: js.Object // You could nest another @JSNative trait here for x, y coordinates } @JSImport("canvas-confetti", "default") external val confetti: js.JSFunction1[ConfettiOptions.type, js.Any] = confetti // Now we can create a typed options object val options = js.Dynamic.literal( particleCount = 150, spread = 70 ).asInstanceOf[ConfettiOptions.type] confetti(options)By using a
@JSNativetrait, you've created a contract. If you accidentally try to pass aStringwhere the JS library expects anInt, the Scala compiler might not catch it (since it's just a facade), but your code is significantly more readable. It documents exactly what the JS library expects without you having to keep the documentation tab open in your browser 24/7.
📋 Practical Task
Integrating the Chart.js Library for Data Visualization
Your task is to create a Scala.js facade for the chart.js library to render a simple bar chart. You will need to handle both the main constructor and the configuration object.
- Requirement 1: Define a
@JSImportfor theChartclass. Note thatChart.jsuses a named export calledChart. - Requirement 2: Create a
@JSNativetrait calledChartConfigthat includes at least thetype(e.g., "bar") anddataproperties. - Requirement 3: Write a Scala function that initializes a new
Chartinstance, passing in a canvas element from the DOM and your typedChartConfigobject. - Requirement 4: Ensure the
dataproperty in your config is properly structured to handlelabelsanddatasetsusingjs.Dynamic.literalor further@JSNativetraits.
Hint: Remember that the Chart constructor expects the canvas element as the first argument and the configuration object as the second.
There are no comments for now.