Type Sets and JSON
A handful of exports sit at the root of src/ rather than in a category. They have one thing in common:
each names a set of types that TypeScript itself leaves unnamed, so a constraint can say “a primitive” or
“anything JSON can hold” instead of spelling the union out again.
PrimitiveTypes
Section titled “PrimitiveTypes”type PrimitiveTypes = boolean | number | string | object | symbol | bigint | Function | undefined | null📘 Every type built into the language.
import type { PrimitiveTypes } from 'type-plus'
type R = 1 extends PrimitiveTypes ? true : false // truetype R = { a: 1 } extends PrimitiveTypes ? true : false // true, via `object`Note that this is broader than the ECMAScript definition of a primitive: object and Function are
included, so the set covers every type, not only the ones stored by value. Use NonComposableTypes below
for the value types alone.
ComposableTypes and NonComposableTypes
Section titled “ComposableTypes and NonComposableTypes”type ComposableTypes = object | Functiontype NonComposableTypes = boolean | number | string | symbol | bigint | undefined | nullThe same set split by whether a value of the type can carry custom properties.
import type { ComposableTypes, NonComposableTypes } from 'type-plus'
type R = { a: 1 } extends ComposableTypes ? true : false // truetype R = (() => void) extends ComposableTypes ? true : false // true
type R = 1 extends NonComposableTypes ? true : false // truetype R = symbol extends NonComposableTypes ? true : false // trueThe distinction matters when a type adds a marker property to its input, which is what branding does. A brand over a composable type intersects; a brand over a non-composable one has to wrap.
UnionKeys
Section titled “UnionKeys”type UnionKeys<T> = keyof T | (T extends unknown ? keyof T : never)⚗️ transform — the keys of T, collected across every member of a union.
import type { UnionKeys } from 'type-plus'
type R = UnionKeys<{ a: 1 } | { b: 2 }> // 'a' | 'b'type R = keyof ({ a: 1 } | { b: 2 }) // neverkeyof on a union gives only the keys every member shares, which for disjoint members is never.
UnionKeys distributes first, so it gives the keys any member has. That is the constraint Omit and
omit use, which is why they work on a union where the built-in Omit does not.
The JSON types
Section titled “The JSON types”type JSONTypes = JSONPrimitive | JSONObject | JSONArraytype JSONPrimitive = boolean | number | string | nulltype JSONObject = { [key in string]?: JSONTypes }type JSONArray = Array<JSONTypes>🧰 type util — the values that survive a JSON round trip.
import type { JSONTypes } from 'type-plus'
type R = { a: 1 } extends JSONTypes ? true : false // truetype R = Date extends JSONTypes ? true : false // falsetype R = undefined extends JSONPrimitive ? true : false // falseUse JSONTypes as the constraint on data that has crossed a JSON boundary. It admits exactly what comes
back, so a Date, a Map, a function or undefined is rejected where it is put in rather than silently
lost on the way out.
Two details follow from that. undefined is absent from JSONPrimitive, because JSON has no such value
and JSON.stringify drops the properties holding it. And every property of JSONObject is optional, so
reading one yields JSONTypes | undefined — the honest type for parsed data, where no key is guaranteed
to be there.
Reference
Section titled “Reference”| Type | Description |
|---|---|
PrimitiveTypes |
every built-in type |
ComposableTypes |
the types that can carry custom properties |
NonComposableTypes |
the types that cannot |
UnionKeys<T> |
keys of T, collected across a union |
JSONTypes |
any value JSON can hold |
JSONPrimitive |
boolean | number | string | null |
JSONObject |
string keys holding JSONTypes, all optional |
JSONArray |
Array<JSONTypes> |
Source: src/composable_types.ts,
src/primitive.ts,
src/union_keys.ts and
src/json.ts.