core-types v3.1.0
core-types
This package provides TypeScript types describing core types useful in TypeScript, JavaScript, JSON, JSON Schema etc. It also contains functions for simplifying unnecessarily complex types, as well helper utilities for other packages converting to/from core-types and another type system.
Using core-types, e.g. implementing conversions to other type systems is easy, since core-types is relatively small and well defined.
- See use cases and other packages using this package
- Usage
- Specification of the types in this package are:
- any (any of the below types)
- null
- boolean (
true,false) - string
- number and integer; distinguished in JSON Schema, equivalent in TypeScript
- object; key-value of core types where the key is a string
- array; list of arbitrary length of a specific core type
- tuple; list of specific length with distinguished core types in each position
- ref a reference to a named type
- unions and intersections of the above
- union;
{ or: [ core-types... ] } - intersection;
{ and: [ core-types... ] }
- union;
The above describes JSON completely, and is a lowest common denominator for describing types useful for JSON, JSON Schema and TypeScript. Think of it as an extremely simplified version of JSON Schema.
See
This package is used by:
core-types-json-schemaconverting to and from JSON Schema / Open API
core-types-tsconverting to and from TypeScript types/interfaces
core-types-graphqlconverting to and from GraphQL
core-types-suretypeconverting to and from SureType validator schemas
typeconvconversion between JSON Schema, TypeScript, GraphQL and Open API through core-types
Versions
Since 3.0 this package is pure ESM and requires Node 14.13.1 or later.
Usage
To create a core-types type, just cast it to NodeType.
import type { NodeType } from 'core-types'
const myStringType: NodeType = { type: 'string' };For more information on the specific types, see the Specification.
simplify
The function simplify can take a type, or an array of types, and returns simplified type definitions.
Examples of simpifications performed:
- An empty
andororwill often be removed. - A union (e.g. of
anyandstring), except for usagesconstandenum, can be simplified as justany. - An intersection of
anyandstringcan be simplified tostring. - An
orcontaining childors, will be flattened to the parentor. - and more...
The simplify function is type-wise lossless, but can remove annotations (e.g. descriptions). It is however usually recommended to perform a simplification after a type has been converted to core-types before converting to another type system.
import { simplify } from 'core-types'
const simplified = simplify( myType );
simplify( {
type: 'or',
or: [
{ type: 'or', or: [ { type: 'string' } ] },
{ type: 'any', const: 'foo' }
]
} ); // { type: 'string', const: 'foo' }validate
The validate function validates that a NodeType type tree is valid.
It ensures e.g.
- Non-negative integer
minItems - Non-mismatching enums and const if both are specified.
import { validate } from 'core-types'
validate( myType ); // Throws error if not validtraverse
The traverse function traverses a type tree and calls a callback function for every node it finds.
The callback function gets an object as argument on the following form:
interface TraverseCallbackArgument
{
node: NodeType;
rootNode: NodeType;
path: Array< string | number >;
parentProperty?: string;
parentNode?: NodeType;
index?: string | number;
required?: boolean;
}import { traverse } from 'core-types'
traverse( rootNode, ( { node } ) => {
if ( !node.title )
node.title = "This is a dummy title";
} );some
The some function is similar to traverse but the callback can return a boolean. If the callback returns true, some returns true, otherwise false.
This is useful to quickly find if a node satisifes a certain criteria, and is similar to Array.prototype.some.
import { some } from 'core-types'
const hasRefNode = some( rootNode, ( { node: { type } } ) => type === 'ref' );helpers
When implementing conversions to and from core-types, the following helper functions may come in handy:
ensureArrayconverts values to arrays of such values, or returns arrays as-is. null and undefined become empty arrayisPrimitiveTypereturns true for primitiveNodeTypeshasConstEnumreturns true forNodeTypes which has (or can have)constandenumproperties.isEqualdeep-equal comparison (of JSON compatible non-recursive types)intersectionreturns an array of values found in both of two arrays. Handles primitives as well as arrays and objects (usesisEqual)unionreturns an array of unique values from two arrays. Handles primitives as well as arrays and objects (usesisEqual)isNonNullableisCoreTypesErrordecorateErrorMetadecorateErrorgetPositionOffsetmergeLocations
Annotations
mergeAnnotationsextractAnnotationsstringifyAnnotationsstripAnnotationsstringify
Conversion
When converting, a conversion package is recommended to return a ConversionResult<T>, i.e. the data as property data in an object which also contains information about the conversion:
interface ConversionResult< T = string >
{
data: T;
convertedTypes: Array< string >;
notConvertedTypes: Array< string >;
}Specification
The main type is called NodeType and is a union of the specific types. A NodeType always has a type property of the type Types. The Types is defined as:
type Types =
| 'any'
| 'null'
| 'boolean'
| 'string'
| 'number'
| 'integer'
| 'object'
| 'array'
| 'tuple'
| 'ref'
| 'and'
| 'or';Depending on which type is used, other properties in NodeType will be required. In fact, the NodeType is defined as:
type NodeType =
| AnyType
| NullType
| BooleanType
| StringType
| NumberType
| IntegerType
| ObjectType
| ArrayType
| TupleType
| RefType
| AndType
| OrType;These types have an optional name (string) property which can be converted to be required using NamedType<T = NodeType>. This is useful when converting to other type systems where at least the top-most types must have names (like JSON Schema definitions or exported TypeScript types/interfaces), and is used by the NodeDocument, which is what conversion packages should use:
interface NodeDocument
{
version: 1; // core-types only has version 1 so far
types: Array< NamedType >;
}The types also have optional annotation properties title (string), description (string), examples (string or array of strings), default (string), see (string or array of strings) and comment (string).
All types except NullType, AndType and OrType can have two properties const (of type T) or enum (of type Array<T>). The T depends on the NodeType. These have the same semantics as in JSON Schema, meaning a const value is equivalent of an enum with only that value. The enum can be seen as a type literal union in TypeScript.
any type
The AnyType matches any type. Its const and enum properties have the element type T set to unknown.
This corresponds to any or unknown in TypeScript, and the empty schema {} in JSON Schema.
Example: { type: 'any' }
null type
The NullType is simply equivalent to the TypeScript, JavaScript and JSON type null.
Example: { type: 'null' }
boolean type
The BooleanType is equivalent to the TypeScript, JavaScript and JSON Boolean (true and false).
The element type T for const and enum is boolean.
Example: { type: 'boolean', const: false }
string type
The StringType is equivalent to the TypeScript, JavaScript and JSON type String.
The element type T for const and enum is string.
Example: { type: 'string', enum: [ "foo", "bar" ] }
number type
core-types distinguishes between NumberType and IntegerType.
In TypeScript, JavaScript and JSON they are both equivalent to Number. In JSON Schema however, integer is a separate type, and can therefore be converted to core-types with maintained type information.
The element type T for const and enum is number.
Example: { type: 'number', enum: [ 17, 42 ] }
object type
The ObjectType is used to describe the TypeScript type Record<string, NodeType> and the JavaScript and JSON type Object. In TypeScript or JavaScript, the keys must only be strings, not numbers or symbols.
The element type T for const and enum is Record<string, any-json-type>, i.e. plain objects.
Two more properties are required for an ObjectType, properties and additionalProperties.
properties is defined as Record<string, { node: NodeType; required: boolean; }>.
additionalProperties is defined as boolean | NodeType. When this is false, no additional properties apart from those defined in properties are allowed, and if true properties are allowed of any type (AnyType). Otherwise additional properties are allowed of the defined NodeType.
Example:
{
type: 'object',
properties: {
name: { node: { type: 'string' }, required: true },
age: { node: { type: 'number' }, required: true },
level: { node: { type: 'string', enum: [ 'novice', 'proficient', 'expert' ] }, required: false },
},
additionalProperties: false,
}array type
The ArrayType is used to describe the TypeScript type Array<NodeType> and the JavaScript and JSON type Array.
The element type T for const and enum is Array<any-json-type>, i.e. arrays of JSON-compatible types defined by the NodeType in elementType.
The extra and required property elementType is of type NodeType and defines what types the array can hold.
Example:
{
type: 'array',
elementType: { type: 'string' },
}tuple type
The TupleType describes specific-length arrays where each position has a specific type. It matches the tuple type [A, B, ...] in TypeScript and is an Array in JavaScript and JSON.
The element type T for const and enum is [...any-json-types], i.e. tuples of JSON-compatible types defined by the NodeType in the required elementTypes and additionalItems.
The extra and required properties for TupleType are elementTypes, minItems and additionalItems.
elementTypes is defined as [...NodeType] and describes the valid types for each position in the tuple for the required and individually typed optional tuple elements.
minItems is an integer (TypeScript/JavaScript number) defining the minimum required elements and must not be negative. If this is greater than the number of elementTypes, although valid in core-types per se, some conversions will limit it to the size of elementTypes.
additionalProperties is used to describe optional extra elemenents. It is defined as boolean | NodeType. When this is false, no additional elements are allowed, and if true elements are allowed of any type (AnyType). Otherwise additional elemenets are allowed of the defined NodeType.
Example:
{
type: 'tuple',
elementTypes: [
{ type: 'string' },
{ type: 'boolean' }, // Optional, because minItems is 1
],
minItems: 1,
additionalItems: { type: 'number' },
}ref type
The RefType describes references to other named types. Exactly what this means is up to the implementation of the user of core-types, but it is recommended that a reference type in a list of NodeTypes refers to a named type within that list. This corresponds to TypeScript named types being referred to in the same file as in which the type is defined, or JSON Schema $ref references only referring to #/definitions/* types.
A RefType has a required property ref which is a string corresponding to the name of the reference.
Example:
[
{
name: 'User',
type: 'object',
properties: {
name: { node: { type: 'string' }, required: true },
id: { node: { type: 'number' }, required: true },
},
additionalProperties: true,
},
{
name: 'UserList',
type: 'array',
elementType: { type: 'ref', ref: 'User' },
},
]union type
The OrType describes a union of other types. This is equivalent to union types in TypeScript (e.g. number | string) and anyOf in JSON Schema.
An OrType has a required property or which is defined as Array<NodeType>.
Example:
{
type: 'or',
or: [
{ type: 'string' },
{ type: 'number' },
{ type: 'ref', ref: 'IdType' }, // Defined somewhere...
},
}intersection type
The AndType describes an intersection of other types. This is equivalent to intersection types in TypeScript (e.g. A & B) and allOf in JSON Schema.
An AndType has a required property and which is defined as Array<NodeType>.
Example:
[
{
name: 'CommentWithId',
type: 'and',
and: [
{ type: 'ref', ref: 'Comment' },
{ type: 'ref', ref: 'WithId' },
},
},
{
name: 'Comment',
type: 'object',
properties: {
line: { node: { type: 'string' }, required: true },
user: { node: { type: 'ref', ref: 'User' }, required: true },
},
additionalProperties: false,
},
{
name: 'WithId',
type: 'object',
properties: {
id: { node: { type: 'string' }, required: true },
},
additionalProperties: false,
},
]3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago