Documentation chapters
You're looking at legacy documentation.
New multi-language documentation available at https://docs.deepkit.io
Type

Reflection

With Deepkit Type it's possible to get reflection information about types during runtime. There are mainly two ways to read the runtime type data: Either via Reflection classes or via raw type objects.

Type objects

Type objects are simple JavaScript objects representing all types of TypeScript. To get a type object from a type, use the typeOf() function.

import { typeOf, ReflectionKind } from '@deepkit/type';

typeOf<string>(); // {kind: ReflectionKind.string}
typeOf<number>(); // {kind: ReflectionKind.number}
typeOf<boolean>(); // {kind: ReflectionKind.boolean}

typeOf<string | number>(); 
// {kind: ReflectionKind.union, types: [{kind: ReflectionKind.string}, {kind: ReflectionKind.number}]}

class MyClass {
    id: number = 0;
}
typeOf<MyClass>();
//{kind: ReflectionKind.class, classType: MyClass, types: [
//    {kind: ReflectionKind.property, name: 'id', type: {kind: ReflectionKind.number}, default: () => 0}
//]}

Reflection classes

There are Reflection classes available mainly for classes and interfaces, and their properties and methods.

import { ReflectionClass } from '@deepkit/type';

class MyClass {
    id: number = 0;

    doIt(arg: string): void {}
}

const reflection = ReflectionClass.from(MyClass);
reflection.getProperty('id').type; // {kind: ReflectionKind.number}
reflection.getProperty('id').isOptional(); //false
reflection.getPropertyNames(): ['id'];

reflection.getMethod('doIt').getReturnType(); //{kind: ReflectionKind.void}
reflection.getMethod('doIt').getParameter('arg').type; //{kind: ReflectionKind.string}

//works with interfaces as well
interface User {
    id: number;
}
const reflection = ReflectionClass.from<User>();

Stringify type

A type object can be printed as TypeScript source. stringifyType tries to print the least possible while stringifyResolvedType prints the full type (including properties etc.)

import { typeOf, stringifyType, stringifyResolvedType } from '@deepkit/type';

interface User {
    id: number;
}

const type = typeOf<User>();
stringifyType(type); //User
stringifyResolvedType(type); //User {id: number}

Type annotations

There are several special types available that annotate the types with various information. For example UUID, PrimaryKey, Group, Excluded, and more. See Special types for more information.

Those special types add annotations to the type object in the annotations property.

import { typeOf, PrimaryKey } from '@deepkit/type';
type MyId = string & PrimaryKey;
const type = typeOf<MyId>();
console.log(type);

This prints a bit of information already:

{
  kind: 5,
  typeName: 'MyId',
  annotations: { [Symbol(primaryKey)]: [ true ] },
  decorators: [
    {
      kind: 30,
      typeName: 'PrimaryKey',
      types: [Array],
      annotations: {}
    }
  ]
}

In decorators the actual decorator type of PrimaryKey can be found, and in annotations the parsed annotation information. The annotation can be read in this case via:

import { isPrimaryKeyType, primaryKeyAnnotation } from '@deepkit/type';

isPrimaryKeyType(type); //true;

primaryKeyAnnotation.getFirst(type); //true

For special types that accept arguments, those values are usually available in one of the *Annotation objects. For example for DatabaseField:

import { DatabaseField, databaseAnnotation } from '@deepkit/type';

type MyField = string & DatabaseField<{type: 'VARCHAR(255)'}>;
const type = typeOf<MyField>();
databaseAnnotation.getFirst(type); //{ name: '*', options: { type: 'VARCHAR(255)' } }

For special types that can be applied multiple times, they are available via the *Annotation.getAnnotations() method.

import { Group, groupAnnotation } from '@deepkit/type';

type MyField = string & Group<'a'> & Group<'b'>
const type = typeOf<MyField>();

groupAnnotation.getAnnotations(type); //[ 'a', 'b' ]

Custom special types

All the special types use the same pattern: They return an object literal with an optional __meta tuple, where the first entry is a unique id and all subsequent entries additional information. The runtime type processor handles those object literals with only an optional __meta property in a special way and does not try to actually intersect the two types. Instead they will be treated as type decorators that generate type annotations.

For example the PrimaryKey looks like that:

export type PrimaryKey = { __meta?: ['primaryKey'] };

It has no additional information. However, a type like MapName has two:

export type MapName<Alias extends string, ForSerializer extends string = ''> = { __meta?: ['mapName', Alias, ForSerializer] };

You can define and use your own special type just like that:

import { metaAnnotation, stringifyType } from '@deepkit/type';

type MyType<T extends string> = { __meta?: ['myType', T] };

type myField = string & MyType<'option'>; 

const type = typeOf<myField>();

stringifyType(type); //string

metaAnnotation.getAnnotations(type);
//[{
//  name: 'myType',
//  options: [ { kind: ReflectionKind.literal, literal: 'option' ]
//}]

With those information now available in metaAnnotation you can change for example the serializer or validator to something special when that type annotation is found.

Made in Germany