Making a SuperStruct EthereumAddress type
SuperStruct is a great library for validating data in Javascript. It provides proper TypeScript types, which has made me favor it to some of the competitors like Joi.
I want it to work well and be as strict as possible for Ethereum addresses (0x...).
Let’s say I’m receiving this token object from some kind of user input and I want to validate it.
const token = {
address: "0x728713b41fcEc5C071359Ecf2802Fa0B62bec5a8"
}With superstruct, we can make a simple schema and validate this.
import { is, object, string } from 'superstruct'
const TokenSchema = object({
address: string(),
})
is(token, TokenSchema) // trueBut string isn’t very specific in this regard. This shouldn’t be possible:
const invalidToken = {
address: "asdfg"
}
is(invalidToken, TokenSchema) // true ❌ SHOULD BE FALSELet’s fix this!
Making a custom EthereumAddress SuperStruct type
Let’s make a custom type in SuperStruct.
import { define } from "superstruct";
const EthereumAddress = define("EthereumAddress", (value) => {
// validation logic here
});How do we validate an Ethereum address? One easy way is by using the Ethers library.
import { utils } from "ethers";
utils.isAddress("0x728713b41fcEc5C071359Ecf2802Fa0B62bec5a8") // true
utils.isAddress("asdf") // falseLet’s put this into our new type.
// Yes, I am using isString from lodash. Too lazy.
import { isString } from "lodash";
const EthereumAddress = define("EthereumAddress", (value) => {
// first check if it is a string
if(!isString(value)){
return false
}
// then check if it is a valid address
return utils.isAddress(value);
});Now we can supercharghe our TokenSchema with our new type.
const TokenSchema = object({
address: EthereumAddress
});
const token = {
address: "0x728713b41fcEc5C071359Ecf2802Fa0B62bec5a8"
}
is(token, TokenSchema) // true
const invalidToken = {
address: "asdfg"
}
is(invalidToken, TokenSchema) // falseGive the right types to typescript
One of my favourite things with SuperStruct is that you can infer types to use in Typescript. But our new EthereumAddress type gives us an unknown 😲.
type Token = Infer<typeof TokenSchema>
// type Token = {
// address: unknown
//}We can fix this by explicitly telling SuperStruct that the closest TypeScript type is string when defining the type.
We do this by using the generic define<string>.
const EthereumAddress = define<string>("EthereumAddress", (value) => {
...
})Now it gives us the proper TypeScript type.
type Token = Infer<typeof TokenSchema>
// type Token = {
// address: string
//}If you want it to be even prettier, you could add an actual EthereumAddress TypeScript type.
type EthereumAddress = string
const EthereumAddress = define<EthereumAddress>("EthereumAddress", (value) => {
...
})
type Token = Infer<typeof TokenSchema>
// type Token = {
// address: EthereumAddress
//}Conclusion
SuperStruct is great by itself, but can be even greater by adding some custom types.
Check out the CodeSandbox.

Hi!
I'm Lars. I write about building scalable systems, software architecture, and programming.
I created Turfemon to be my playground. A place where I can dive into technical topics, share quick insights, and document my learning journey, without contaminating my main site's pristine collection of 'profound' insights.
Working on something exciting? Send me an email: show email
