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-devor yarn:
yarn add ts-objectify-type -DTypeScript >= 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-patchas a dev-dependency with npm:
npm install ts-patch --save-devor 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.jsonset a path to the transformer in thecompilerOptionspluginsarray:
// 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
ttypescriptas a dev-dependency with npm:
npm install ttypescript --save-devor yarn:
yarn add ttypescript -D
- In
tsconfig.jsonset a path to the transformer in thecompilerOptionspluginsarray:
// tsconfig.json
{
"compilerOptions": {
"plugins": [
{ "transform": "ts-objectify-type/transformer" }
]
}
}- To build project use
ttscinstead 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:
keystring | number | symbol - Name of the propertyrequiredboolean - Whether property is marked as requiredtypeobjectified.TypeValue (string) - Type of the property. Main idea behind it is to represent whattypeofkeyword 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",
}
];
nullis technically primitive, but whentypeofis 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
tupleTypeobjectified.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
argumentsobjectified.Property[] - an array of function arguments representationsreturnTypeobjectified.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
unionOfobjectified.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
intersectionOfobjectified.Type[] - type representation of individual intersection members
Note that
primitivesIntersectionis set asstring & numberbut they have no common properties and typescript treats it as aneverand 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
referenceNamestring - name of the referenced typepropsobjectified.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
typeArgumentsobjectified.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,
isCircularboolean - 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
aliasRefis set asAliasType, thereferenceNameis 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)- haskeyandrequiredpropertiesisRequired(type)-requiredistrueisOptional(type)-requiredisfalse
isString(type)- whether is stringisNumber(type)- whether is numberisBoolean(type)- whether is booleanisUndefined(type)- whether is undefinedisNull(type)- whether is null
isFunction(type)- whether is function
isUnion(type)- whether is unionisIntersection(type)- whether is intersection
isArray(type)- whether is arrayisTuple(type)- whether is tupleisArrayOrTuple(type)- whether is array or tuple
isLiteralObject(type)- whether is literal objectisReference(type)- whether is referenceisCircularReference(type)- whether is circular referencehasProps(type)- whether it haspropsproperty (meaning reference or literal object)isUnknownObject(type)- unknown object represents error or unsupported type (please report a bug when encountered)
isVoid(type)- whether is voidisNever(type)- whether is neverisGenericParameter(type)- whether is generic type parameter
isNotNullPrimitive(type)- string, number, boolean or undefinedisPrimitive(type)- "Not Null Primitive", or nullisNotObject(type)- "Not Null Primitive", void or neverisNotNullObject(type)- literal object, array, tuple reference, unknown objectisObject(type)- Not Null Object or null