Building Safe JavaScript Interoperability in PureScript: A Practical FFI Guide
Learn how to safely bridge PureScript and JavaScript using Maybe types and effect types to maintain type safety when calling foreign functions.
21 Aug 2026, 11:37 UTC

The Challenge of Safe FFI in PureScript
When integrating PureScript code with existing JavaScript ecosystems, the real challenge isn't just calling JS functions—it's doing so without sacrificing the type safety that makes PureScript valuable. Many developers hit a wall when their carefully typed PureScript code encounters JavaScript's dynamic nature, leading to runtime errors that could have been caught at compile time.
Prerequisites: Setting Up Your Environment
Before diving into FFI declarations, ensure you have:
- PureScript installed globally:
npm install -g purescript - A package manager like
spagofor project management - Basic understanding of PureScript's type system
Initialize a new project:
spago init my-ffi-project
The Core Problem: Handling JavaScript Null and Undefined
JavaScript's null and undefined values are the primary source of type safety violations when using FFI. PureScript's type system doesn't natively represent these values, creating a gap that must be bridged carefully.
Solution: Using Maybe for Nullable Values
The most reliable approach is to use PureScript's Maybe type to represent potentially absent values. This transforms runtime uncertainty into compile-time type safety.
Foreign Import Declaration
Create a FFI module that properly handles optional values:
foreign import unsafeGetProperty :: forall a. String -> Object a -> Maybe a
JavaScript Implementation
In your corresponding JavaScript file, implement the function to return undefined or a value:
exports.unsafeGetProperty = function(key, obj) {
return obj[key];
};
Working with Effect Types
For operations that interact with the JavaScript runtime, use effect types to make side effects explicit:
foreign import readFromLocalStorage :: String -> Effect (Maybe String)
This declaration tells the compiler that the function performs an effect and may return nothing.
Verification: Checking Generated JavaScript
After building your project with spago build, examine the generated JavaScript in output/ directory. Look for your FFI bindings to ensure they're correctly imported and that the type signatures match your expectations.
Common Pitfalls and Recovery Options
Pitfall 1: Direct property access without null checking
Recovery: Always wrap property access in a Maybe-returning function
Pitfall 2: Forgetting to handle Promise resolution in async FFI
Recovery: Use Effect.Aff for asynchronous operations
Best Practices for Maintainable FFI
- Minimize FFI surface area: Keep foreign imports to a minimum and wrap them in pure PureScript functions
- Document assumptions: Add comments explaining what JavaScript behavior you're relying on
- Test edge cases: Verify behavior with null, undefined, and unexpected types
Conclusion
By using Maybe for nullable values and effect types for side effects, you can maintain PureScript's type safety guarantees while interoperating with JavaScript. The key is making the dynamic nature of JavaScript explicit in your type signatures rather than hiding it.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.