TypeScript transformer for creating object representation of deeply nested types.
The library provides a custom typescript transformer that creates object representation of provided Type.
To transform a type use objectifyType
from the ts-objectify-type
module:
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
//...
}
const obj = objectifyType<ExampleType>();
Returned object is of type objectified.TypeRepresentation
, which is alias for objectified.Property[]
.
objectified
namespace provides all types for the object returned by the objectifyType
function, and can be also imported from the ts-objectify-type
.
Check examples for more information.
Install ts-objectify-type
with npm:
npm install ts-objectify-type --save-dev
or yarn:
yarn add ts-objectify-type -D
TypeScript >= 2.4.1
TypeScript provides limited support for using custom transformers, but it doesn't have built-in, easy-to-use options for this purpose. However, there are alternative solutions available.
The easiest solution is to use a community-driven library called ts-patch, which offers a way to overcome this limitation and utilize custom transformers with TypeScript projects. The package is build based on ttypescript (for more info check next section).
- Install
ts-patch
as a dev-dependency with npm:
npm install ts-patch --save-dev
or yarn:
yarn add ts-patch -D
- Add prepare script (keeps patch persisted after npm install)
// package.json
{
"scripts": {
"prepare": "ts-patch install -s"
}
}
- In
tsconfig.json
set a path to the transformer in thecompilerOptions
plugins
array:
// tsconfig.json
{
"compilerOptions": {
"plugins": [
{ "transform": "ts-objectify-type/transformer" }
]
}
}
TTypescript (Transformer TypeScript) solves the problem by patching on the fly the compile module to use transformers from tsconfig.json
.
Instead of tsc
and tsserver
, use ttsc
and ttsserver
wrappers.
These wrappers try to use locally installed typescript first.
ttypescript
is also compatible with Webpack and Rollup (check ttypescript repo for more info)
- Install
ttypescript
as a dev-dependency with npm:
npm install ttypescript --save-dev
or yarn:
yarn add ttypescript -D
- In
tsconfig.json
set a path to the transformer in thecompilerOptions
plugins
array:
// tsconfig.json
{
"compilerOptions": {
"plugins": [
{ "transform": "ts-objectify-type/transformer" }
]
}
}
- To build project use
ttsc
instead oftsc
. All arguments work the same way
ttsc
- ts-loader (Webpack)
- awesome-typescript-loader (Webpack)
- rollup-plugin-typescript2 (Rollup)
There are several type groups, each represented by an interface. The interfaces are designed to provide the most information from the corresponding type.
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
prop: string;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
"key": "prop",
"required": true,
"type": "string"
}
]
There are 3 base properties that every "objectified" property has:
-
key
string | number | symbol - Name of the property -
required
boolean - Whether property is marked as required -
type
objectified.TypeValue (string) - Type of the property. Main idea behind it is to represent whattypeof
keyword would return when used on the type, but with few extra. You can check all of them in examples section.
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
requiredProp: string;
optionalProp?: string;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: 'requiredProp',
required: true,
type: 'string'
},
{
key: 'optionalProp',
required: false,
type: 'string'
}
];
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
primitiveVoid: void;
primitiveNever: never;
primitiveNumber: number;
primitiveString: string;
primitiveBoolean: boolean;
primitiveUndefined: undefined;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: 'primitiveVoid',
required: true,
type: 'void'
},
{
key: 'primitiveNever',
required: true,
type: 'never'
},
{
key: 'primitiveNumber',
required: true,
type: 'number'
},
{
key: 'primitiveString',
required: true,
type: 'string'
},
{
key: 'primitiveBoolean',
required: true,
type: 'boolean'
},
{
key: 'primitiveUndefined',
required: true,
type: 'undefined'
}
];
Generated properties type: objectified.PrimitiveType
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
literalObj: {
prop1: string;
prop2: number;
};
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "literalObj",
required: true,
type: "object",
objectType: "literal",
props: [
{
key: "prop1",
required: true,
type: "string"
},
{
key: "prop2",
required: true,
type: "number"
}
]
}
];
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
nullProp: null;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "nullProp",
required: true,
type: "object",
objectType: "null",
}
];
null
is technically primitive, but whentypeof
is used, it returns"object"
.
So in generated objecttype="object"
andobjectType="null"
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
simpleArray: string[];
classArray: Array<string>;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "simpleArray",
required: true,
type: "object",
objectType: "array",
arrayType: {
type: "string"
}
},
{
key: "classArray",
required: true,
type: "object",
objectType: "array",
arrayType: {
type: "string"
}
}
];
Generated properties type: objectified.ArrayType
-
arrayType
(objectified.Type) - represents type of the array
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
simpleTuple: [string, number?];
namedTuple: [
one: string,
two?: number
];
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "simpleTuple",
required: true,
type: "object",
objectType: "tuple",
tupleType: [
{
key: 0,
required: true,
type: "string"
},
{
key: 1,
required: false,
type: "number"
}
]
},
{
key: "namedTuple",
required: true,
type: "object",
objectType: "tuple",
tupleType: [
{
key: "one",
required: false,
type: "string"
},
{
key: "two",
required: true,
type: "number"
}
]
}
];
Generated properties type: objectified.TupleType
-
tupleType
objectified.Property[] - an array representation of tuple individual types
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
voidFn: () => void;
argsFn: (arg1: string, arg2: number) => string;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "voidFn",
required: true,
type: "function",
arguments: [],
returnType: {
type: "void"
}
},
{
key: "argsFn",
required: true,
type: "function",
arguments: [
{
key: "arg1",
required: true,
type: "string"
},
{
key: "arg2",
required: true,
type: "number"
}
],
returnType: {
type: "string"
}
}
];
Generated property type: objectified.FunctionType
-
arguments
objectified.Property[] - an array of function arguments representations -
returnType
objectified.Type - function return type representation
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
primitivesUnion: string | number;
objsUnion: { a: string } | { b: number };
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "primitivesUnion",
required: true,
type: "union",
unionOf: [
{
type: "string"
},
{
type: "number"
}
]
},
{
key: "objsUnion",
required: true,
type: "union",
unionOf: [
{
type: "object",
objectType: "literal",
props: [
{
key: "a",
required: true,
type: "string"
}
]
},
{
type: "object",
objectType: "literal",
props: [
{
key: "b",
required: true,
type: "number"
}
]
}
]
}
];
Generated properties type: objectified.UnionType
-
unionOf
objectified.Type[] - type representation of individual union members
import { objectifyType } from 'ts-objectify-type';
interface ExampleType {
primitivesIntersection: string & number; // = never
objsIntersection: { a: string } & { b: number };
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "primitivesIntersection",
required: true,
type: "never"
},
{
key: "objsIntersection",
required: true,
type: "intersection",
intersectionOf: [
{
type: "object",
objectType: "literal",
props: [
{
key: "a",
required: true,
type: "string"
}
]
},
{
type: "object",
objectType: "literal",
props: [
{
key: "b",
required: true,
type: "number"
}
]
}
]
}
];
Generated properties type: objectified.IntersectionType
-
intersectionOf
objectified.Type[] - type representation of individual intersection members
Note that
primitivesIntersection
is set asstring & number
but they have no common properties and typescript treats it as anever
and same is returned when objectified.
import { objectifyType } from 'ts-objectify-type';
export interface SimpleInterface {
prop: string;
}
interface ExampleType {
simpleRef: SimpleInterface;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "simpleRef",
required: true,
type: "object",
objectType: "reference",
referenceName: "SimpleInterface",
props: [
{
key: "prop",
required: true,
type: "string"
}
]
}
];
Generated property type: objectified.ReferenceType
-
referenceName
string - name of the referenced type -
props
objectified.Type[] - same as in literal object, the nested properties are represented.
import { objectifyType } from 'ts-objectify-type';
export interface InterfaceWithGeneric<T> {
prop: T;
}
interface ExampleType {
genericRef: InterfaceWithGeneric<string>;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "refWithGeneric",
required: true,
type: "object",
objectType: "reference",
referenceName: "InterfaceWithGeneric",
typeArguments: [
{
type: "string"
}
],
props: [
{
key: "prop",
required: true,
type: "string"
}
]
}
];
Generated property type: objectified.ReferenceType
-
typeArguments
objectified.Type[] - an array of generic parameters representations
import { objectifyType } from 'ts-objectify-type';
export interface InterfaceCircular {
selfRef: InterfaceCircular;
}
interface ExampleType {
circularRef: InterfaceCircular;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "refCircular",
required: true,
type: "object",
objectType: "reference",
referenceName: "InterfaceCircular",
props: [
{
key: "selfRef",
required: true,
type: "object",
objectType: "reference",
referenceName: "InterfaceCircular",
isCircular: true
}
]
}
];
Generated properties type: objectified.ReferenceType
,
-
isCircular
boolean - indicates whether the property references a type that has already been referenced earlier in the same object
import { objectifyType } from 'ts-objectify-type';
export interface SimpleInterface {
prop: string;
}
type AliasType = SimpleInterface;
interface ExampleType {
aliasRef: AliasType;
}
const obj = objectifyType<ExampleType>();
// Generated result:
const obj = [
{
key: "aliasRef",
required: true,
type: "object",
objectType: "reference",
referenceName: "SimpleInterface",
props: [
{
key: "prop",
required: true,
type: "string"
}
]
}
];
Generated properties type: objectified.ReferenceType
,
Note that even though the type of
aliasRef
is set asAliasType
, thereferenceName
is directlySimpleInterface
The following utility functions are available in the objectified
namespace to narrow down objectified.Type
.
All functions take one argument of objectified.Type
type.
-
isProperty(type)
- haskey
andrequired
properties -
isRequired(type)
-required
istrue
-
isOptional(type)
-required
isfalse
-
isString(type)
- whether is string -
isNumber(type)
- whether is number -
isBoolean(type)
- whether is boolean -
isUndefined(type)
- whether is undefined -
isNull(type)
- whether is null
-
isFunction(type)
- whether is function
-
isUnion(type)
- whether is union -
isIntersection(type)
- whether is intersection
-
isArray(type)
- whether is array -
isTuple(type)
- whether is tuple -
isArrayOrTuple(type)
- whether is array or tuple
-
isLiteralObject(type)
- whether is literal object -
isReference(type)
- whether is reference -
isCircularReference(type)
- whether is circular reference -
hasProps(type)
- whether it hasprops
property (meaning reference or literal object) -
isUnknownObject(type)
- unknown object represents error or unsupported type (please report a bug when encountered)
-
isVoid(type)
- whether is void -
isNever(type)
- whether is never -
isGenericParameter(type)
- whether is generic type parameter
-
isNotNullPrimitive(type)
- string, number, boolean or undefined -
isPrimitive(type)
- "Not Null Primitive", or null -
isNotObject(type)
- "Not Null Primitive", void or never -
isNotNullObject(type)
- literal object, array, tuple reference, unknown object -
isObject(type)
- Not Null Object or null