fontcolor_theme
Deepkit Runtime Types

型アノテーション

型アノテーションは、実行時に読み取られ、さまざまな関数の挙動を変更できるメタ情報を含む通常の TypeScript の Type です。Deepkit には、すでに多くのユースケースをカバーする型アノテーションが用意されています。例えば、Class の Property を主キー、参照、インデックスとしてマークできます。データベースライブラリは、事前のコード生成なしに、この情報を実行時に使用して正しい SQL クエリを作成できます。

MaxLengthMaximumPositive といったバリデータ制約も任意の型に追加可能です。特定の値をどのようにシリアライズ/デシリアライズするかをシリアライザに指定することもできます。さらに、完全にカスタムな型アノテーションを作成し、それらを実行時に読み取ることで、型システムを非常に個別的な方法で実行時に利用することも可能です。

Deepkit には一連の型アノテーションが付属しており、すべて @deepkit/type から直接使用できます。これらは複数のライブラリからではなく、Deepkit RPC や Deepkit Database といった特定のライブラリにコードを直接結び付けないように設計されています。これにより、たとえデータベース型アノテーションを使用していても、フロントエンドでの型の再利用が容易になります。

以下は既存の型アノテーションの一覧です。@deepkit/type@deepkit/bson のバリデータとシリアライザ、そして @deepkit/orm の Deepkit Database は、この情報をそれぞれ異なる方法で使用します。詳細は対応する章を参照してください。

Integer/Float

整数と浮動小数点は、基底として number で定義され、いくつかのサブバリアントがあります:

integer任意サイズの整数。
int8-128 から 127 までの整数。
uint80 から 255 までの整数。
int16-32768 から 32767 までの整数。
uint160 から 65535 までの整数。
int32-2147483648 から 2147483647 までの整数。
uint320 から 4294967295 までの整数。
floatnumber と同じですが、データベースの文脈では異なる意味を持つ場合があります。
float32-3.40282347e+38 から 3.40282347e+38 の間の float。JavaScript は精度の問題により範囲を正しく検証できないことに注意してください。ただし、この情報はデータベースやバイナリシリアライザには有用です。
float64number と同じですが、データベースの文脈では異なる意味を持つ場合があります。
import { integer } from '@deepkit/type';

interface User {
    id: integer;
}

ここで、ユーザーの id は実行時には number ですが、バリデーションやシリアライゼーションでは整数として解釈されます。 つまり、例えばバリデーションでは float は使用できず、シリアライザは自動的に float を整数に変換します。

import { is, integer } from '@deepkit/type';

is<integer>(12); //true
is<integer>(12.5); //false

サブタイプも同様に使用でき、許可する数値範囲を特定したい場合に有用です。

import { is, int8 } from '@deepkit/type';

is<int8>(-5); //true
is<int8>(5); //true
is<int8>(-200); //false
is<int8>(2500); //false
import { is, float, float32, float64 } from '@deepkit/type';
is<float>(12.5); //true
is<float32>(12.5); //true
is<float64>(12.5); //true

UUID

UUID v4 は通常、データベースではバイナリとして、JSON では文字列として保存されます。

import { is, UUID } from '@deepkit/type';

is<UUID>('f897399a-9f23-49ac-827d-c16f8e4810a0'); //true
is<UUID>('asd'); //false

MongoID

このフィールドを MongoDB の ObjectId としてマークします。解決結果は文字列です。MongoDB ではバイナリとして保存されます。

import { MongoId, serialize, is } from '@deepkit/type';

serialize<MongoId>('507f1f77bcf86cd799439011'); //507f1f77bcf86cd799439011
is<MongoId>('507f1f77bcf86cd799439011'); //true
is<MongoId>('507f1f77bcf86cd799439011'); //false

class User {
    id: MongoId = ''; //ユーザーが挿入されると Deepkit ORM によって自動的に設定されます
}

Bigint

デフォルトでは通常の bigint 型は JSON では number(BSON では long)としてシリアライズされます。しかし、JavaScript の bigint は潜在的に無制限のサイズを持つのに対し、JavaScript の number や BSON の long は制限があるため、保存できる内容には制約があります。この制限を回避するために BinaryBigIntSignedBinaryBigInt が利用可能です。

BinaryBigInt は bigint と同じですが、データベースでは無制限サイズの符号なしバイナリ(多くのデータベースの 8 バイトではなく)として、JSON では文字列としてシリアライズされます。負の値は正の値に変換されます(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 は BinaryBigInt をバイナリフィールドとして保存します。

SignedBinaryBigIntBinaryBigInt と同様ですが、負の値も保存できます。Deepkit ORM は SignedBinaryBigInt をバイナリとして保存します。このバイナリには先頭に符号バイトが追加され、符号なし整数(uint)として表現されます: 負は 255、ゼロは 0、正は 1。

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

interface User {
    id: SignedBinaryBigInt;
}

MapName

シリアライズ時に Property 名を変更します。

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

Property をグループ化できます。シリアライズでは、例えば特定のグループを除外できます。詳細はシリアライゼーションの章を参照してください。

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

interface Model {
    username: string;
    password: string & Group<'secret'>
}

serialize<Model>(
    { username: 'Peter', password: 'nope' },
    { groupsExclude: ['secret'] }
); //{username: 'Peter'}

Data

各 Property は、リフレクション 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

各 Property は、特定のターゲットに対するシリアライズ処理から除外できます。

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。deserialize のデフォルトのシリアライザは `json` であるため

item.password = 'secret';

const json = serialize<Auth>(item);
json.password; //再び undefined。serialize のシリアライザは `json` のため

Embedded

フィールドを埋め込み型としてマークします。

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'
}

//deserialize では埋め込み構造を提供する必要があります
deserialize<User>({
    id: 12,
    address_street: 'abc',
    //...
});

プレフィックス(デフォルトでは Property 名)を変更することも可能です。

interface User {
    id: number & PrimaryKey;
    address: Embedded<Address, { prefix: 'addr_' }>;
}

serialize<User>(user);
{
    id: 12,
        addr_street
:
    'abc',
        addr_postalCode
:
    '1234',
}

//または完全に削除する
interface User {
    id: number & PrimaryKey;
    address: Embedded<Address, { prefix: '' }>;
}

serialize<User>(user);
{
    id: 12,
        street
:
    'abc',
        postalCode
:
    '1234',
}

Entity

Interface にエンティティ情報を注釈します。データベースの文脈でのみ使用されます。

import { Entity, PrimaryKey } from '@deepkit/type';

interface User extends Entity<{ name: 'user', collection: 'users'> {
    id: number & PrimaryKey;
    username: string;
}

PrimaryKey

フィールドを主キーとしてマークします。データベースの文脈でのみ使用されます。

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

interface User {
    id: number & PrimaryKey;
}

AutoIncrement

フィールドをオートインクリメントとしてマークします。データベースの文脈でのみ使用されます。 通常は PrimaryKey と併用します。

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

interface User {
    id: number & PrimaryKey & AutoIncrement;
}

Reference

フィールドを参照(外部キー)としてマークします。データベースの文脈でのみ使用されます。

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

interface User {
    id: number & PrimaryKey;
    group: number & Reference<Group>;
}

interface Group {
    id: number & PrimaryKey;
}

この例では User.group は所有側の参照で、SQL における外部キーとしても知られています。つまり、User テーブルには group カラムがあり、Group テーブルを参照しています。Group テーブルはこの参照のターゲットテーブルです。

BackReference

フィールドを逆参照としてマークします。データベースの文脈でのみ使用されます。

interface User {
    id: number & PrimaryKey;
    group: number & Reference<Group>;
}

interface Group {
    id: number & PrimaryKey;
    users: User[] & BackReference;
}

この例では Group.users は逆参照です。これは、User テーブルに group カラムがあり、それが Group テーブルを参照していることを意味します。 Group は仮想の Property users を持ち、結合を伴うデータベースクエリが実行されると、Group の id と同じ group id を持つすべてのユーザーで自動的に埋められます。Property users はデータベースには保存されません。

Index

フィールドをインデックスとしてマークします。データベースの文脈でのみ使用されます。

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

interface User {
    id: number & PrimaryKey;
    username: string & Index;
}

Unique

フィールドをユニークとしてマークします。データベースの文脈でのみ使用されます。

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

interface User {
    id: number & PrimaryKey;
    username: string & Unique;
}

DatabaseField

DatabaseField を使うと、実際のデータベースのカラム型やデフォルト値など、データベース固有のオプションを定義できます。

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

interface User {
    id: number & PrimaryKey;
    username: string & DatabaseField<{ type: 'varchar(255)' }>;
}

Validation

TODO

バリデーション制約の型を参照してください。

InlineRuntimeType

ランタイム型をインライン化します。高度なケースでのみ使用されます。

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

const type: Type = { kind: ReflectionKind.string };

type Query = {
    field: InlineRuntimeType<typeof type>;
}

const resolved = typeOf<Query>(); // { field: string }

TypeScript では型 Query{ field: any } ですが、実行時には { field: string } です。

これは、ランタイム型を受け取り、さまざまな場面で再利用するような高いカスタマイズ性を持つシステムを構築する場合に有用です。

ResetAnnotation

Property のすべてのアノテーションをリセットします。高度なケースでのみ使用されます。

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

interface User {
    id: number & PrimaryKey;
}

interface UserCreationPayload {
    id: User['id'] & ResetAnnotation<'primaryKey'>;
}

カスタム型アノテーション

独自の型アノテーションを定義できます。

type MyAnnotation = { __meta?: ['myAnnotation'] };

慣例として、型アノテーションは単一の省略可能な Property __meta を持つオブジェクトリテラルとして定義され、その型はタプルです。このタプルの最初の要素が一意の名前で、以降の要素は任意のオプションです。これにより、型アノテーションに追加のオプションを装備できます。

type AnnotationOption<T extends { title: string }> = { __meta?: ['myAnnotation', T] };

型アノテーションは交差型演算子 & と共に使用します。1 つの型に任意の数の型アノテーションを使用できます。

type Username = string & MyAnnotation;
type Title = string & MyAnnotation & AnnotationOption<{ title: 'Hello' }>;

型アノテーションは、typeOf<T>()typeAnnotation の型オブジェクトを介して読み取れます:

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

const type = typeOf<Username>();
const annotation = typeAnnotation.getForName(type, 'myAnnotation'); //[]

annotation の結果は、型アノテーション myAnnotation が使用されていればオプションを持つ配列、使用されていなければ undefined です。AnnotationOption に見られるように、型アノテーションに追加オプションがある場合、渡された値はその配列の中に見つかります。 すでに提供されている MapNameGroupData などの型アノテーションには、それぞれ専用のアノテーションオブジェクトがあります:

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

type Username = string & Group<'a'> & Group<'b'>;

const type = typeOf<Username>();
groupAnnotation.getAnnotations(type); //['a', 'b']

詳しくはランタイム型のリフレクションを参照してください。

English中文 (Chinese)한국어 (Korean)日本語 (Japanese)Deutsch (German)