Types
Deepkit Type supports almost all TypeScript types out of the box. It could be a simple string, number, boolean, or more complex classes and interfaces, as well as computed types based on type functions like Partial
, and even generics.
Since Deepkit Type uses the runtime type information made available by the type compiler of @deepkit/type-compiler, there is nothing special you have to write to make the types available in runtime.
How to use
On of the common use cases of deepkit type is casting data types. Serialization, validation, and database access are other use cases. All of them have in common that you pass the type as type argument to the function.
So, if you have for example a string
type and want to convert or validate a string the code would look like that:
import { cast, is } from '@deepkit/type'; cast<string>(24); //becomes '24' is<string>(24); //false is<string>('24'); //true interface User { id: number; username: string; } cast<User>({id: '24', username: 'Peter'}); //becomes {id: 24, username: 'Peter'} is<User>({id: '24', username: 'Peter'}); //false is<User>({id: 24, username: 'Peter'}); //true
Since the first type argument of cast
could be any type, you could also write:
import { cast } from '@deepkit/type'; interface User { id: number; username: string; } cast<User['username']>(24); //becomes '24' is<User>({id: 0}); //false is<User>({id: , username: 'Peter'}); //true is<Partial<User>>({id: 0}); //true
Or use other type functions.
import { cast } from '@deepkit/type'; interface User id: string; createdAt: Date; username: string; firstName?: string; lastName?: string; } cast<Omit<User, 'id' | 'createdAt'>({username: 'Peter'}); //{username: 'Peter'} //or use aliases type UserCreate = Omit<User, 'id' | 'createdAt'>; cast<UserCreate>({username: 'Peter'});
import { Maximum, Minimum } from '@deepkit/type'; type ID = number & Minimum<0> & Maximum<100>; is<ID>(0); //true is<ID>(120); //false
Special types
Deepkit Type comes with some special types as well as type annotations to enrich your types with additional information and behaviour.Integer/Float
Using one of the integer/float types ensures during deserializing and validation that a value is in the correct range. The information is also available in runtime and allows for example Deepkit ORM to use the correct table column type.
import { cast, integer, int8, uint8 } from '@deepkit/type'; cast<integer>(12.4); //becomes 12 cast<integer>(12); //stays 12 cast<int8>(127); //stays 127 cast<int8>(233); //becomes 127 cast<uint8>(-12); //becomes 0 cast<uint8>(300); //becomes 255
import { validates, integer, uint8 } from '@deepkit/type'; validates<integer>(12); //true validates<integer>(12.4); //false, since not an integer validates<uint8>(420); //false, since max is 255
Type | Description |
---|---|
integer | An integer of arbitrary size. |
int8 | An integer between -128 and 127. |
uint8 | An integer between 0 and 255. |
int16 | An integer between -32768 and 32767. |
uint16 | An integer between 0 and 65535. |
int32 | An integer between -2147483648 and 2147483647. |
uint32 | An integer between 0 and 4294967295. |
float | Same as number , but might have different meaning in database context. |
float32 | A float between -3.40282347e+38 and 3.40282347e+38. Note that JavaScript is not able to check correctly the range due to precision issues, but the information might be handy for the database or binary serializers. |
float64 | Same as number , but might have different meaning in database context. |
UUID
UUID v4 format. Can be combined with default value of uuid()
. Deepkit ORM tries to save the UUID in a column type that is native to the database (usually as binary).
import { UUID, serialize, is, uuid } from '@deepkit/type'; serialize<UUID>('d06d7e9f-f229-479d-a1ee-67d17baa26d7'); //d06d7e9f-f229-479d-a1ee-67d17baa26d7 is<UUID>('d06d7e9f-f229-479d-a1ee-67d17baa26d7'); //true is<UUID>('abcd'); //false class User { id: UUID = uuid(); }
MongoId
Marks this field as ObjectId for MongoDB. Resolves as a string. Is stored in the MongoDB as binary.
import { MongoId, serialize, is } from '@deepkit/type'; serialize<MongoId>('507f1f77bcf86cd799439011'); //507f1f77bcf86cd799439011 is<MongoId>('507f1f77bcf86cd799439011'); //true is<MongoId>('507f1f77bcf86cd799439011'); //false class User { id: MongoId = ''; //will automatically set in Deepkit ORM once user is inserted }
BinaryBigInt
Per default the normal bigint
type serializes as number in JSON (and long in BSON). This has however limitation in what is possible to save as bigint in JavaScript has a unlimited potential size, where numbers in JavaScript and long in BSON are limited. To bypass this limitation the BinaryBigInt
is available.
Same as bigint
but serializes to unsigned binary with unlimited size (instead of 8 bytes in most databases) in databases and string in JSON. Negative values will be converted to positive (abs(x)).
import { BinaryBigInt } from '@deepkit/type'; interface User { id: BinaryBigInt; } const user: User = {id: 24n}; serialize<User>({id: 24n}); //{id: '24'} serialize<BinaryBigInt>(24); //'24' serialize<BinaryBigInt>(-24); //'0'
Deepkit ORM stores BinaryBigInt as a binary field.
SignedBinaryBigInt
Same as BinaryBigInt
but is able to store negative values as well.
Deepkit ORM stores SignedBinaryBigInt as binary. The binary has an additional leading sign byte and is represented as an uint: 255 for negative, 0 for zero, or 1 for positive.
import { SignedBinaryBigInt } from '@deepkit/type'; interface User { id: SignedBinaryBigInt; }
MapName
To change the name of a property in the serialization.
import { serialize, deserialize, MapName } from '@deepkit/type'; interface User { firstName: string & MapName<'first_name'>; } serialize<User>({firstName: 'Peter'}) // {first_name: 'Peter'} deserialize<User>({first_name: 'Peter'}) // {firstName: 'Peter'}
Group
Properties can be grouped together. For serialization you can for example exclude a group from serialization. See the chapter Serialization for more information.
import { serialize } from '@deepkit/type'; interface Model { username: string; password: string & Group<'secret'> } serialize<Model>( { username: 'Peter', password: 'nope' }, { groupsExclude: ['secret'] } ); //{username: 'Peter'}
Data
Each property can add additional meta data that can be read via the Reflection API.
import { ReflectionClass } from '@deepkit/type'; interface Model { username: string; title: string & Data<'key', 'value'> } const reflection = ReflectionClass.from<Model>(); reflection.getProperty('title').getData()['key']; //value;
Excluded
Each property can be excluded from the serialization process for a specific target.
import { serialize, deserialize, Excluded } from '@deepkit/type'; interface Auth { title: string; password: string & Excluded<'json'> } const item = deserialize<Auth>({title: 'Peter', password: 'secret'}); item.password; //undefined, since deserialize's default serializer is called `json` item.password = 'secret'; const json = serialize<Auth>(item); json.password; //again undefined, since serialize's serializer is called `json`
Entity
To annotate interfaces with entity information. Only used in the database context.
import { Entity, PrimaryKey } from '@deepkit/type'; interface User extends Entity<{name: 'user', collection: 'users'> { id: number & PrimaryKey; username: string; }
PrimaryKey
Marks this field as primary key. Only used in the database context.
import { PrimaryKey } from '@deepkit/type'; interface User { id: number & PrimaryKey; username: string; }
AutoIncrement
Marks this field as auto increment. The field is usually also a primary key. Only used in the database context.
import { PrimaryKey, AutoIncrement } from '@deepkit/type'; interface User { id: number & PrimaryKey & AutoIncrement; username: string; }
Reference
Marks this field as a reference. Also known as Foreign Key in database context.
import { PrimaryKey, Reference } from '@deepkit/type'; interface Image { id: number & PrimaryKey; path: string; url: string; } interface User { id: number & PrimaryKey; avatar: Image & Reference; }
BackReference
Marks this field as a back reference. Needed for the reversed side of a reference.
import { PrimaryKey, Reference, BackReference } from '@deepkit/type'; interface Image { id: number & PrimaryKey; path: string; url: string; users: User[] & BackReference; } interface User { id: number & PrimaryKey; avatar: Image & Reference; }
Index
Marks the field as an index.
import { PrimaryKey, Index } from '@deepkit/type'; interface User { id: number & PrimaryKey; username: string & Index; }
Unique
Marks the field as an unique index.
import { PrimaryKey, Unique } from '@deepkit/type'; interface User { id: number & PrimaryKey; username: string & Unique; }
Embedded
Marks the field as an embedded type.
import { PrimaryKey, Embedded, serialize, deserialize } from '@deepkit/type'; interface Address { street: string; postalCode: string; city: string; country: string; } interface User { id: number & PrimaryKey; address: Embedded<Address>; } const user: User { id: 12, address: { street: 'abc', postalCode: '1234', city: 'Hamburg', country: 'Germany' } }; serialize<User>(user); { id: 12, address_street: 'abc', address_postalCode: '1234', address_city: 'Hamburg', address_country: 'Germany' } //for deserialize you have to provide the embedded structure deserialize<User>({ id: 12, address_street: 'abc', //... });
It's possible to change the prefix (which is per default the property name).
interface User { id: number & PrimaryKey; address: Embedded<Address, {prefix: 'addr_'}>; } serialize<User>(user); { id: 12, addr_street: 'abc', addr_postalCode: '1234', } //or remove it entirely interface User { id: number & PrimaryKey; address: Embedded<Address, {prefix: ''}>; } serialize<User>(user); { id: 12, street: 'abc', postalCode: '1234', }
DatabaseField
This type allows to specify certain options to configure the database column/field definition. For example it enables you to define an explicit SQL database type, e.g. VARCHAr(255)
.
There are more specific versions of this type available via MySQL
, Postgres
, SQLite
import { MySQL, SQLite, DatabaseField } from '@deepkit/type'; class User { username: string & MySQL<{ type: 'varchar(255)' }> = ''; username: string & SQLite<{ type: 'varchar(255)' }> = ''; //same as above but for all databases username: string & DatabaseField<{ type: 'varchar(255)' }> = ''; }
InlineRuntimeType
This type allows to inject a dynamic built type object into the type system. The type processor includes the given variable content instead of inferring its type.
import { InlineRuntimeType, Type, ReflectionKind, typeOf, stringifyType } from '@deepkit/type'; const typeObject: Type = { kind: ReflectionKind.string }; interface MyType a: InlineRuntimeType<typeof typeObject>; } const type = typeOf<MyType>(); stringifyType(type); //MyType {a: string}
Explicit types
The type compiler only recognizes explicit type declarations, no inferring. That means if you want have types available in runtime make sure to explicitly define them. This is in most cases only necessary for class properties and function parameters.
class User { id = 0; //not typed id: number = 0; //correctly typed }
Validation
To add additional validation checks there are several pre defined decorators available. To add custom validation functions and learn more about validation, see the chapter Validation.
Type decoration | Description |
---|---|
Validate<typeof myValidator> | Custom validation function |
Pattern<typeof myRegexp> | Defines a regular expression as validation pattern. Usually used for E-Mail validation or more complex content validation. |
Alpha | Validation for alpha characters (a-Z). |
Alphanumeric | Validation for alpha and numeric characters. |
Ascii | Validation for ASCII characters |
Decimal<1, 34> | Validation for string represents a decimal number, such as 0.1, .3, 1.1, 1.00003, 4.0, etc. |
MultipleOf<3> | Validation of numbers that are a multiple of given number. |
MinLength<3>, MaxLength<4> | Validation for min/max length for arrays or strings of given number. |
Includes<'something'> Excludes<'something'> | Validation for an array item being included/excluded. |
Minimum<2>, Maximum<4> | Validation for a value being minimum or maximum given number. Same as >= <= . |
ExclusiveMinimum<3>, ExclusiveMaximum<3> | Same as minimum/maximum but exludes the value itself. Same as > < |
Positive, Negative, PositiveNoZero, NegativeNoZero | Validation for a value being positive or negative. |
BeforeNow, AfterNow | Validation for a date value compared to now (new Date). |
Simple regexp validation of emails via /^\S+@\S+$/ . |
More information about validators can be found in the chapter Validation
External classes
Since TypeScript does not include type information per default, imported types/classes from other packages (that did not use @deepkit/type-compiler) will not have type information available.
To annotate types for an external class, use annotateClass
and make sure this function is executed in the bootstrap phase of your application before the imported class is used somewhere else.
import { MyExternalClass } from 'external-package'; import { annotateClass } from '@deepkit/type'; interface AnnotatedClass { id: number; title: string; } annotateClass<AnnotatedClass>(MyExternalClass); //all uses of MyExternalClass return now the type of AnnotatedClass serialize<MyExternalClass>({...}); //MyExternalClass can now also be used in other types interface User { id: number; clazz: MyExternalClass; }
MyExternalClass can now be used in serialization functions and in the reflection API.
To following shows how to annotate generic classes
import { MyExternalClass } from 'external-package'; import { annotateClass } from '@deepkit/type'; class AnnotatedClass<T> { id!: T; } annotateClass(ExternalClass, AnnotatedClass);
Not supported types
Not supported types are at the moment:- Key remapping in Mapped Types, e.g.
{ [P in keyof T & string as `${P}Change`]: T[P] }