
Security News
Static vs. Runtime Reachability: Insights from Latio’s On the Record Podcast
The Latio podcast explores how static and runtime reachability help teams prioritize exploitable vulnerabilities and streamline AppSec workflows.
typescript-checker
Advanced tools
Powerful data type validation library enabling type safety
const validBody = {
name: "Dodo",
age: 42,
meta: {
canFly: false,
pickupItems: ["egg", "grass"],
},
}
const invalidBody = {
name: null,
age: 42,
meta: [false, true],
}
const checkBody = Keys({
name: And(TypeString, MinLength(2)),
age: TypeNumber,
meta: Keys({
canFly: TypeBoolean,
pickupItems: Items(OneOf("egg", "grass", "stone")),
}),
})
const checkedValue = check(checkBody, validBody) // valid, returns value
const checkedValue = check(checkBody, invalidBody) // throws CheckerError
type IBody = CheckerSuccess<typeof checkBody>
// equals type/interface
type IBody = {
name: string
age: number
meta: {
canFly: boolean
pickupItems: ("egg" | "grass" | "stone")[]
}
}
// there are more ways to execute a checker and handle errors, see the detailed API docs!
Requires TypeScript 3.x or higher
npm i typescript-checker
OR
yarn add typescript-checker
A Checker
is a function to which a value
can be passed, which should be validated. The Checker
itself can be a base type checker like TypeString
, a more complex checker with nested properties like Keys
or Items
, or a chain of different (logically connected) checker like And(TypeString, MinLength(2))
or And.then(Fallback(TypeString, () => "[]")).then(parseFilterQuery)
.
The Checker
can be invoked like a function, returning a tuple [errors] | [null, validValue]
. Either errors
contains a list of errors, or validValue
returns the validated and stripped value. Both cases can be conveniently checked via isCheckError(<result>)
and isCheckValid(<result>)
.
Alternatively, you can use the provided check(<checker>, <value>)
function throwing a JavaScript Error
, which can be try/catch
ed.
const validBody = {
name: "Dodo",
age: 42,
meta: {
canFly: false,
pickupItems: ["egg", "grass"],
},
}
const invalidBody = {
name: null,
age: 42,
meta: [false, true],
}
const checkBody = Keys({
name: And(TypeString, MinLength(2)),
age: TypeNumber,
meta: Keys({
canFly: TypeBoolean,
pickupItems: Items(OneOf("egg", "grass", "stone")),
}),
})
const checkResultValid = checkBody(validBody) // returns [null, validBody] indicating a valid checker result
const checkResultInvalid = checkBody(invalidBody) // returns [['.name expected string found null'], null] indicating an error checker result
if (isCheckValid(checkResultValid)) {
// ...true
}
if (isCheckError(checkResultInvalid)) {
// ...true
}
try {
const value = check(checkBody, validBody)
console.log(value) // outputs <value>
} catch (err) {
console.error(err) // not executed
}
try {
const value = check(checkBody, invalidBody)
console.log(value) // not executed
} catch (err) {
console.error(err) // err is CheckerError
if (err instanceof CheckerError) {
consoe.error(err.errors) // logs [['.name expected string found null']]
}
}
TypeUndefined
TypeNull
TypeString
TypeBoolean
TypeNumber
TypeUnknown
TypeFunction
TypeObject
TypeArray
TypeEnum
(typescript only)
enum Status {
Pending,
Accepted,
Closed,
}
const checkStatus = TypeEnum(Status)
TypeEnumString
(typescript only)
enum Status {
Pending = "pending",
Accepted = "accepted",
Closed = "closed",
}
const checkStatus = TypeEnumString(Status)
TypeMatches
(tests for a regex match)
TypeParseInt
(string which holds an integer value, returns string)
TypeParseFloat
(string which holds an integer value, returns string)
TypeParseBoolean
(string which holds an boolean value, returns string)
TypeParseDate
(string which holds an stringified Date value, returns string)
ConvertParseInt
(string which holds an integer value, returns number)
ConvertParseFloat
(string which holds a float value, returns number)
ConvertParseBoolean
(string which holds a boolean value, returns boolean)
ConvertDate
(string/number representing a valid Date, returns Date)
And(checkerA, checkerB)
like function composition (if checkerA succeeds, check the result using checkerB)
And(TypeString, MinLength(10)) // string which is at least 10 characters long
Or(checkerA, checkerB, ...)
check union type (if checkerA fails, check original value using checkerB)
Or(TypeNull, TypeString) // accepts a string or null
Or(TypeUndefined, TypeNumber) // accepts a number or undefined
Keys
checks for an object containing certain keys with their corresponding value type
const checkBody = Keys({
name: And(TypeString, MinLength(2)),
age: TypeNumber,
})
const checkNestedBody = Keys({
name: And(TypeString, MinLength(2)),
age: Or(TypeUndefined, TypeNumber), // property that must exist, but that may be undefined
meta: Keys({
canFly: TypeBoolean,
}),
})
const checkOptionalBody = Keys(
{
name: And(TypeString, MinLength(2)),
age: TypeNumber, // optional property
},
["age"],
)
Items
checks for an array containing certain item types
Items(TypeString) // array containing only string values
Items(TypeNumber) // array containing only number values
Items(Keys({ name: And(TypeString, MinLength(2)) })) // array containing only objects with a <name> property of type string
Items(Items(TypeNumber)) // 2d array containing only number values
Items(Or(TypeString, TypeBoolean)) // array containing strings or booleans, e.g. ["hello world", false, true]
OneOf
checks for defined literals
OneOf(42, 1337) // only value 42 or 1337 is accepted
OneOf("dodo", "raptor") // only value "dodo" or "raptor" is accepted
OneOf("dodo", 42, false) // only value "dodo" or 42 or false is accepted
Fallback
provides a way to take a default value if the checked value is undefined
const myChecker = Fallback(TypeSting, () => "Take this as default value")
const validResult = myChecker("Hello World") // validResult is "Hello World"
const fallbackResult = myChecker(undefined) // fallbackResult is "Take this as default value"
Catch
provides a way to take a default value if the checker fails
const myChecker = Catch(TypeSting, () => "Take this as default value")
const validResult = myChecker("Hello World") // validResult is "Hello World"
const fallbackResult = myChecker(42) // fallbackResult is "Take this as default value"
withDefault
is not a checker, but takes a checker result and a default value
const myChecker = TypeSting
const validResult = withDefault(myChecker("Hello World"), "Take this as default value") // validResult is "Hello World"
const fallbackResult = withDefault(myChecker(42), "Take this as default value") // fallbackResult is "Take this as default value"
ConvertJSON
takes a string value and tries to parse its JSON content
const myJSONChecker = ConvertJSON
const validObject = myJSONChecker('{"a": 42}') // validObject is {a: 42}
const validArray = myJSONChecker("[1,2,3,4,5]") // validArray is [1,2,3,4,5]
const invalidResult = myJSONChecker("dodo") // invalidResult holds an error
checkPair
takes any value and checks if it's a pair with certain types
const simplePairChecker = checkPair(TypeString, TypeNumber) // [string, number]
simplePairChecker(["Hello", 42]) // valid
simplePairChecker([42, "Hello"]) // error
simplePairChecker(["Hello", 42, true]) // error, expects array of length 2
// use a custom defined tuple type
type CustomTuple = [string, boolean]
const customTupleTypeChecker = checkPair<CustomTuple>(TypeString, TypeBoolean)
MinLength(number)
checks if a given string has a minimum lengthMaxLength(number)
checks if a given string has a maximum lengthMin(number)
checks if a given number is greater-equals a certain valueMax(number)
checks if a given number is less-equals a certain valueBetween(number, number)
checks if a given number is between two given valuesIsUUID
checks if a given string is a valid uuid (v1, v4, or v5)EMail
checks if a given string is a valid email addressAnd
provides an API for chaining checkers.
// Example 1
// takes a stringified JSON, parses it, and on success checks keys of the JSON object
const parseStringJSONPayload = And.then(TypeString)
.then(ConvertJSON)
.then(
Keys({
answer: TypeString,
freeTextEnabled: TypeBoolean,
next: TypeNumber,
}),
)
// Example 2
interface IFilterItem {
key: string
values: (string | number)[]
}
type FilterItems = IFilterItem[]
const checkFilterItem = Keys<IFilterItem>({ key: TypeString, values: Items(Or(TypeString, TypeNumber)) })
const checkFilterItems = Items(checkFilterItem)
const parseFilterQuery: Checker<string, FilterItems> = (value) => {
try {
const data = JSON.parse(value)
const checkResult = checkFilterItems(data)
return checkResult
} catch (err) {
return [err.message]
}
}
// if value is undefined, we want to provide a fallback value before continue checking by parsing the
// stringified JSON by a custom checker
const checkPaginationFilter = And.then(Fallback(TypeString, () => "[]")).then(parseFilterQuery)
By using the Checker<A,B>
type you can build whatever checker you want.
// a custom Moment checker
const TypeMoment: Checker<unknown, Moment> = checkInstanceOf(Moment, "Moment")
// a custom checker for all uppercase letters
const checkAllUppercase: Checker<string, string> = (value) => {
if (value === value.toUpperCase()) {
return [null, value]
} else {
return [[`expected string with all uppercase letters, found ${value}`]]
}
}
The type CheckerSuccess
enables you to infer the type of a checker.
const checkBody = Keys({
name: And(TypeString, MinLength(2)),
age: TypeNumber,
meta: Keys({
canFly: TypeBoolean,
pickupItems: Items(OneOf("egg", "grass", "stone")),
}),
})
type IBody = CheckerSuccess<typeof checkBody>
// equals type/interface
type IBody = {
name: string
age: number
meta: {
canFly: boolean
pickupItems: ("egg" | "grass" | "stone")[]
}
}
If you prefer to declare your interfaces and types separately, you can just provide the type to the Cast checker adapter to make sure your checked types are equivalent. This also works for union types and optional members.
type Body = {
name: string
age: number
meta: {
canFly: boolean
pickupItems: ("egg" | "grass" | "stone")[]
}
}
// everything ok
const checkBody1 = Cast<Body>().as(
Keys({
name: And(TypeString, MinLength(2)),
age: TypeNumber,
meta: Keys({
canFly: TypeBoolean,
pickupItems: Items(OneOf("egg", "grass", "stone")),
}),
}),
"same",
)
// compiler error -> key "meta" is missing
const checkBody2 = Cast<Body>().as(
// @ts-expect-error
Keys({
name: And(TypeString, MinLength(2)),
age: TypeNumber,
}),
"same",
)
type Egg = {
kind: "egg"
weight?: number
}
type Grass = {
kind: "grass"
}
type Pickup = Egg | Grass
// compiler error -> key "weight" is missing
const checkSomeEgg1 = Cast<Egg>().as(
Keys({
kind: OneOf("egg"),
}),
// @ts-expect-error
"same",
)
const checkSomeEgg2 = {} as Checker<unknown, Egg>
// compiler error -> missing Grass
const checkSomePickup = Cast<Pickup>().as(
Or(checkSomeEgg2),
// @ts-expect-error
"same",
)
When you manually override the generics of any checker constructor, the success type and the set of values that are accepted will differ. This will cause problems, when you use the success type to infer interface types. Therefore you should use Cast
to create a checker adapter that only accepts checkers that symmetricaly matches the success type.
// ❗ never explicitly pass generics to any checker constructor
// it might work as expected (at first)
{
type Body = {
name: string
age: number
meta: {
canFly: boolean
pickupItems: ("egg" | "grass" | "stone")[]
}
}
// no compiler error
const checkBody1 = Keys<Body>({
name: And(TypeString, MinLength(2)),
age: TypeNumber,
meta: Keys({
canFly: TypeBoolean,
pickupItems: Items(OneOf("egg", "grass", "stone")),
}),
})
// compiler error -> key "meta" is missing in checker
const checkBody2 = Keys<Body>({
name: And(TypeString, MinLength(2)),
age: TypeNumber,
})
}
// it may fail badly when you least expect it
{
type Egg = {
type: "egg"
color: "red" | "yellow"
weight?: number
}
type Grass = {
type: "grass"
}
type Pickup = Egg | Grass
const checkSomeEgg = Keys<Egg>({
type: OneOf("egg"),
color: OneOf("red"), // 💥 missing "yellow"
// 💥 missing weight (for typescript-checker < 2)
})
const checkSomePickup = Or<Pickup>(checkSomeEgg) // 💥 missing checkGrass
}
express
integrationgraphql
integrationThis project is licensed under the MIT license.
FAQs
Powerful data validation library enabling type safety
We found that typescript-checker 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.
Security News
The Latio podcast explores how static and runtime reachability help teams prioritize exploitable vulnerabilities and streamline AppSec workflows.
Security News
The latest Opengrep releases add Apex scanning, precision rule tuning, and performance gains for open source static code analysis.
Security News
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.