Shamelessly stealing and converting @theScottyJam 's idea on my previous post to a top-level topic:
I know it's common for a function that expects a string to take whatever parameter it receives and coerce it into a string, as a first step, like this:
function concat(x_, y_) {
const x = String(x_)
const y = String(y_)
return x + y
}
What if, at any location where you're binding a value to a variable, you're allowed to pass the incoming value through a normalization function first, through, say, a "from" keyword (~I don't like this "from" word - feel free to bikeshed it~ edit: I like
from
!). That would make the above example equivalent to this:
function concat(x from String, y from String) {
return x + y
}
It would also allow you to automatically coerce an unknown value to an error.
try {
foo();
} catch (e from Error) {
console.log(e.stack);
}
// ... is the same as ...
try {
foo();
} catch (e_) {
let e = Error(e_)
console.log(e.stack);
}
Some more usage examples :
const normalizedDegrees = deg => deg % 360
function toRadians(deg from normalizedDegrees) {
// ...
}
const positiveNumberAssertion = value => {
if (value <= 0) throw new Error('Whoops!')
return value
}
function doOperation(x from positiveNumberAssertion) {
// ...
}
Use existing type validation libraries:
import { z } from 'zod'
const User = z.object({
name: z.string(),
email: z.string().email(),
})
const greetUser = (user from User.parse) => {
console.log(`Hello ${user.name}, your email is ${user.email}`)
}
Catch only a certain class of errors:
const rethrowIfNot = type => err => {
if (err instanceof type) {
return err
}
throw err
}
try {
foo();
} catch (err from rethrowIfNot(TypeError)) {
console.log('type error!')
}
With IDEs/tools that provide type-hints, the parameter could have the inferred type of the return value of the "coercer" function, without having to introduce a _temp
variable with an unknown type.