Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
@coderspirit/nominal
Advanced tools
Nominal
provides a powerful toolkit to apply
nominal typing on
Typescript with zero runtime overhead.
It offers three kinds of nominal types:
While each type can only have either a brand or a flavor, we can easily combine brands or flavors with properties.
# With NPM
npm install --save-dev @coderspirit/nominal
# Or with PNPM
pnpm add --save-dev @coderspirit/nominal
# Or with Yarn:
yarn add --dev @coderspirit/nominal
[!TIP] Note that if you are developing an application, it is fine to install
@coderspirit/nominal
as a development dependency, but it might be necessary to install it as a normal or peer dependency in case you are developing your own libraries based on it.
import { WithBrand } from '@coderspirit/nominal'
type Email = WithBrand<string, 'Email'>
type Username = WithBrand<string, 'Username'>
const email: Email = 'admin@acme.com' as Email // Ok
const user: Username = 'admin' as Username // Ok
const text: string = email // OK
const anotherText: string = user // Ok
const eMail: Email = 'admin@acme.com' // Error, as we don't have a cast here
const mail: Email = user // Error, as the brands don't match
import { WithFlavor } from '@coderspirit/nominal'
type Email = WithFlavor<string, 'Email'>
type Username = WithFlavor<string, 'Username'>
const email: Email = 'admin@acme.com' as Email // Ok
const user: Username = 'admin' as Username // Ok
const text: string = email // OK
const anotherText: string = user // Ok
const eMail: Email = 'admin@acme.com' // Ok, flavors are more flexible than brands
const mail: Email = user // Error, as the flavors don't match
The types WithBrand
and WithFlavor
, although quite simple in their purpose,
hide a quite complex machinery that exists for the sole purpose of maintaining
full compatibility with other more complex types such as WithProperty
.
Most times we won't really need to rely on such complex mechanisms because we
apply WithBrandh
and WithFlavor
to basic types. So, if we want to minimize
our compilation types, we can chose a simpler and faster implementation:
import {
FastBrand,
FastFlavor,
WithBrand,
WithFlavor
} from '@coderspirit/nominal'
// These two types are 100% equivalent, but the second one takes less time to be
// compiled. Notice that they are 100% equivalent only because they were applied
// to "basic" types (without other associated metadata, like `WithProperty`).
type SlowEmailType = WithBrand<string, 'Email'>
type FastEmailType = FastBrand<string, 'Email'>
// Same for flavors.
type SlowPhoneNumberType = WithFlavor<string, 'PhoneNumber'>
type FastPhoneNumberType = FastFlavor<string, 'PhoneNumber'>
To define a new type with a property, we can do:
import { WithProperty } from '@coderspirit/nominal'
type Even = WithProperty<number, 'Parity', 'Even'>
const myEven: Even = 42 as Even
If we want to use the properties as simple tags, we can omit the property value,
and it will implicitly default to true
, although it's less flexible:
import { WithProperty } from '@coderspirit/nominal'
type Positive = WithProperty<number, 'Positive'>
const myPositive: Positive = 1 as Positive
WithProperty
is additive, commutative and idempotent.WithProperty
can be combined in two ways, which are completely compatible:
&
type operator:
type PositiveEven = WithProperty<number, 'Parity', 'Even'> & WithProperty<number, 'Positive'>
type PositiveEven = WithProperty<WithProperty<number, 'Positive'>, 'Parity', 'Even'>
If we want, we can even define "property types", to ensure that we don't set invalid values:
import { PropertyTypeDefinition, WithStrictProperty } from '@coderspirit/nominal'
type Parity = PropertyTypeDefinition<'Parity', 'Even' | 'Odd'>
// == WithProperty<number, 'Parity', 'Even'>
type Even = WithStrictProperty<number, Parity, 'Even'>
// == never
type Wrong = WithStrictProperty<number, Parity, 'Seven'>
This feature can be very useful when we need to verify many properties for the same value and we don't want to lose this information along the way as the value is passed from one function to another.
function throwIfNotEven<T extends number>(v: T): WithProperty<T, 'Parity', 'Even'> {
if (v % 2 == 1) throw new Error('Not Even!')
return v as WithProperty<T, 'Even'>
}
function throwIfNotPositive<T extends number>(v: T): WithProperty<T, 'Sign', 'Positive'> {
if (v <= 0) throw new Error('Not positive!')
return v as WithProperty<T, 'Positive'>
}
const v1 = 42
// typeof v2 === WithProperty<number, 'Parity', 'Even'>
const v2 = throwIfNotEven(v1)
// typeof v3 extends WithProperty<number, 'Parity', 'Even'>
// typeof v3 extends WithProperty<number, 'Sign', 'Positive'>
const v3 = throwIfNotPositive(v2)
In the previous example, we could add many properties because we were just making assertions about the values. When we transform the passed values, we must be more careful about what we preserve.
As a simple example of what we are telling here, we can see that adding 1
to a
numeric variable would flip its parity, so in that case we wouldn't want to keep
that property on the return value.
type Even<N extends number = number> = WithProperty<N, 'Parity', 'Even'>
type Odd<N extends number = number> = WithProperty<N, 'Parity', 'Odd'>
// 1. 'Parity' is overwritten (when available)
// 2. 'Sign' is kept only if it's positive
// 3. We discard all other properties because they might stop being true
type PlusOneResult<N> = KeepProperties<
N extends Even
? KeepPropertyIfValueMatches<Odd<N>, 'Sign', 'Positive'>
: N extends Odd
? KeepPropertyIfValueMatches<Even<N>, 'Sign', 'Positive'>
: KeepPropertyIfValueMatches<N, 'Sign', 'Positive'>,
'Sign' | 'Parity'
>
function plusOne<N extends number>(v: N): PlusOneResult<N> {
return v + 1 as PlusOneResult<N>
}
FAQs
Powerful nominal types for your project
The npm package @coderspirit/nominal receives a total of 2,131 weekly downloads. As such, @coderspirit/nominal popularity was classified as popular.
We found that @coderspirit/nominal demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.