
Research
Security News
The Landscape of Malicious Open Source Packages: 2025 Mid‑Year Threat Report
A look at the top trends in how threat actors are weaponizing open source packages to deliver malware and persist across the software supply chain.
simple-runtypes
Advanced tools
I said I want **SIMPLE** runtypes. Just functions that validate and return data. Combine them into complex types and TypeScript knows their structure. That's how runtypes work.
I said I want SIMPLE runtypes. Just functions that validate and return data. Combine them into complex types and TypeScript knows their structure. That's how runtypes work.
npm install simple-runtypes
or yarn add simple-runtypes
import * as st from 'simple-runtypes'
const userRuntype = st.record({
id: st.integer(),
name: st.string(),
email: st.optional(st.string()),
})
now, ReturnType<typeof userRuntype>
is equivalent to
interface {
id: number,
name: string,
email?: string
}
userRuntype({id: 1, name: 'matt'})
// => {id: 1, name: 'matt'}
userRuntype({id: 1, name: 'matt', isAdmin: true})
// throws an st.RuntypeError: "invalid field 'isAdmin' in data"
Invoke a runtype with use
to get a plain value back instead of throwing errors:
st.use(userRuntype, {id: 1, name: 'matt'})
// => {ok: true, result: {id: 1, name: 'matt'}}
st.use(userRuntype, {id: 1, name: 'matt', isAdmin: true})
// => {ok: false, error: FAIL}
st.getFormattedError(FAIL)
// => 'invalid keys in record: ["isAdmin"] at `<value>` in `{"id":1,"name": "matt", ... }`'
Not throwing errors is way more efficient and less obscure. Throwing errors and catching them outside is more convenient.
Why should I use this over the plethora of other runtype validation libraries available?
eval
, a small footprint and no dependencies@moltar has done a great job comparing existing runtime type-checking libraries in moltar/typescript-runtime-type-benchmarks.
@pongo has benchmarked simple-runtypes
against io-ts
in pongo/benchmark-simple-runtypes.
A Runtype
is a function that:
RuntypeError
when validation fails
or returns ValidationResult
when passed to use
interface Runtype<T> {
(v: unknown) => T
}
Runtypes are constructed by calling factory functions.
For instance, string
creates and returns a string runtype.
Check the factory functions documentation for more details.
When using record
, any properties which are not defined in the runtype will cause the runtype to fail:
const strict = st.record({name: st.string()})
strict({name: 'foo', other: 123})
// => RuntypeError: Unknown attribute 'other'
To ignore single properties, use ignore
, unknown
or any
:
const strict = st.record({name: st.string(), other: st.ignore()})
strict({name: 'foo', other: 123})
// => {name: foo, other: undefined}
Use sloppyRecord
to only validate known properties and remove everything else:
const sloppy = st.sloppyRecord({name: st.string()})
sloppy({name: 'foo', other: 123, bar: []})
// => {name: foo}
Using any of record
or sloppyRecord
will keep you safe from any __proto__
injection or overriding attempts.
Use the optional
runtype to create optional properties:
const squareConfigRuntype = st.record({
color: st.optional(st.string()),
width?: st.optional(st.number()),
})
Collection runtypes such as record
, array
, tuple
take runtypes as their parameters:
const nestedRuntype = st.record({
name: st.string(),
items: st.array(st.record({ id: st.integer, label: st.string() })),
})
nestedRuntype({
name: 'foo',
items: [{ id: 3, label: 'bar' }],
}) // => returns the same data
simple-runtypes
supports Discriminating Unions via the union
runtype.
The example found in the TypeScript Handbook translated to simple-runtypes
:
const networkLoadingState = st.record({
state: st.literal('loading'),
})
const networkFailedState = st.record({
state: st.literal('failed'),
code: st.number(),
})
const networkSuccessState = st.record({
state: st.literal('success'),
response: st.record({
title: st.string(),
duration: st.number(),
summary: st.string(),
})
})
const networdStateRuntype = st.union(
networkLoadingState,
networkFailedState,
networkSuccessState,
)
type NetworkState = ReturnType<typeof networkStateRuntype>
Finding the runtype to validate a specific discriminating union with is done efficiently with a Map
.
Write your own runtypes as plain functions, e.g. if you want to turn a string into a BigInt
:
const bigIntStringRuntype = st.string({match: /^-?[0-9]+n$/})
const bigIntRuntype = st.runtype((v) => {
const stringCheck = st.use(bigIntStringRuntype, v)
if (!stringCheck.ok) {
return stringCheck.error
}
return BigInt(stringCheck.result.slice(0, -1))
})
bigIntRuntype("123n") // => 123n
bigIntRuntype("2.2") // => error: "expected string to match ..."
Basic runtypes that match JavaScript/TypeScript types:
Meta runtypes:
Objects and Array Runtypes:
Combinators:
union
intersection
omit
pick
partial
get
- similar to Type[key]Shortcuts:
size
- a meta-runtype that imposes a size limit on types, maybe via convert-to-json and .length on the value passed to itstringLiteralUnion
to literals
or literalUnion
and make it work
on all types that literal
acceptssimple-runtypes
is feature complete because it can
simple-runtypes
in productionRuntype
and InternalRuntype
and type runtype internals
(see this comment)7.1.3
FAQs
I said I want **SIMPLE** runtypes. Just functions that validate and return data. Combine them into complex types and TypeScript knows their structure. That's how runtypes work.
We found that simple-runtypes demonstrated a not healthy version release cadence and project activity because the last version was released 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
A look at the top trends in how threat actors are weaponizing open source packages to deliver malware and persist across the software supply chain.
Security News
ESLint now supports HTML linting with 48 new rules, expanding its language plugin system to cover more of the modern web development stack.
Security News
CISA is discontinuing official RSS support for KEV and cybersecurity alerts, shifting updates to email and social media, disrupting automation workflows.