Type definitions

Types are defined in a schema of a database. A type definition can be written as a single complex statement or as multiple simpler statements to the same effect. See the table below for the most simple statements that can be used for type definition in TypeQL.

Table 1. Type definitions
Name Syntax Example

Subtyping entity type

<label> sub <type>;

employee sub person;

Subtyping relation type

<label> sub <type>;

car-ownership sub ownership;

Subtyping attribute type

<label> sub <type>;

full-name sub name;

Adding a role

<relation> relates <label>;

permission relates access;

Assigning a role for a type

<label> plays <relation>:<role>;

access plays permission:access;

Adding ownership to a type

<label> owns <attr-type>;

person owns full-name;

For readability purposes, by default, we recommend to include them in the following order:

  • Attribute types

  • Entity types

  • Relation types

  • Rules

Indentation and line breaks have no effect on the result of a query and are needed only for readability.


Subtyping lets you define a new type, by specifying its parent type (also called its supertype). You can create subtypes of built-in root types: attribute, entity, and relation.

Subtyping root types

credential sub attribute, value string;
subject sub entity;
access sub relation,
    relates action,
    relates object;

Alternatively, you can define a subtype of another user-defined type, either existing in the current schema or defined in the same query:

Subtyping user-defined types

user sub subject;
person sub user;


To define an attribute type, you need to declare its value type. That is done by adding a comma separated value keyword.

Attributes definition

credential sub attribute, value string;
review-date sub attribute, value datetime;
size-kb sub attribute, value long;
validity sub attribute, value boolean;

The following value types are supported in TypeDB:

  • long — a 64-bit signed integer number

  • double — a double-precision floating point number, including a decimal point

  • string — a string enclosed in double " or single ' quotes

  • boolean — a true or false

  • datetime — a date or date-time in one of the following formats:

    • yyyy-mm-dd

    • yyyy-mm-ddThh:mm

    • yyyy-mm-ddThh:mm:ss

    • yyyy-mm-ddThh:mm:ss.f

    • yyyy-mm-ddThh:mm:ss.ff

    • yyyy-mm-ddThh:mm:ss.fff

Restrict values with regular expression

A value of an attribute type can be restricted using regular expressions. For example, to constrain it to a set of options:

Regular expression value restriction

visibility sub attribute, value string, regex "^(public|private|closed)$";

The above example defines an attribute type: visibility. It specifies a regex to restrict values to only public, private, and closed strings.


Relation types are defined by subtyping the relation root type or other relation types.

Roles are added with the relates keyword followed by the role label. At least one role must be defined for any relation.

Relations can play roles in other relations.

Relation with two roles

access sub relation,
    relates object,
    relates action;

For any instance to be able to play a role in a relation, the type of the instance must have that role defined as playable by it with the plays keyword. For more information on letting a type to play a role in a relation type, see the Roleplaying section.


Optionally, types can be defined as abstract. Abstract types can’t be instantiated but can be queried, as their subtypes can be concrete types.

Define abstract type example

subject sub entity, abstract;

You can declare an existing schema type as abstract to make it abstract if there is no data of this type in a database. You can declare an abstract type non-abstract by sending the same type definition without the abstract keyword.


To add the ability for a type to own instances of an attribute type, use the owns keyword followed by the label of the attribute type.

Ownership definitions can be defined as separate statements or combined with general owner type definition with a comma separator:


full-name sub attribute, value string;
object-type sub attribute, value string;

person sub user;
person owns full-name;

object sub entity, owns object-type;

The above definition adds an ability for instances of person type to own instances of the full-name type.


By default, an instance of a type can have ownership of multiple attributes of the same attribute type with different values.

By having multiple attributes of the same type we’re effectively creating a multivalued attribute (as if having an attribute with multiple values).

Use the Key attribute annotation to limit the cardinality to exactly one and add a uniqueness constraint. Hence, the instance of the type with key annotated ownership will have exactly one (no more and no less) key attribute instance.

See the example in the Key attribute section.

More flexible cardinality annotation is expected to be available with the release of TypeDB v.3.0.


Annotations are added for an ownership of an attribute type in a schema to add constraints on what attributes can be owned by an instance of an owner type.

Unique attribute annotation

Use the @unique keyword to add uniqueness as a constraint to the ownership of an attribute. This prevents two instances of the same type from owning the same attribute instance (with the same value).


object sub entity, owns object-type @unique;

The uniqueness constraint can be inherited, even using override of an ownership:

See example

organisation sub entity,
    owns organisation-id @unique;

organisation-id sub attribute, abstract, value string;

non-profit sub organisation,
    owns nonprofit-id as organisation-id;

nonprofit-id sub organisation-id, value string;

In this example non-profit owns nonprofit-id with unique constraint. It’s inherited from organizatrion-id.

Key attribute annotation

Use the @key keyword to set the owned attribute as a key. That limits the cardinality to exactly one and adds the uniqueness constraint. Thus, any instance of a type whose ownership of an attribute is defined using the @key annotation will have exactly one (neither more nor fewer) key attribute instance, and that attribute instance will be unique for all instances of that same owning type.


object sub entity, owns object-type @key;

An ownership can’t have both @unique and @key annotations at the same time.


For any instance to be able to play a role in a relation, the type of the instance must be defined as a potential roleplayer:

Roleplayer type

access sub relation, relates object;
object sub entity, plays access:object;

The above examples define the object type as being able to play the role object in the access relation type. With this definition in a schema, we can now insert data into our database about a relation of type access with the role object played by a given instance of an entity type object.


A subtype inherits its parent’s owned attributes and roles.


path sub attribute, value string;

object sub entity,
    owns object-type,
    plays access:object;
resource sub object, abstract;
file sub resource, owns path;

In the above example, the resource and file entity types are subtypes of the object type, which itself is a subtype of the entity root type. They inherit the object-type attribute type ownership as well as access:object role. However, while the resource subtype is abstract, the file subtype is not. Hence, we can create entities of file type, but not resource entities.

Further, the path attribute type will be owned by the file entity type directly.

Override inherited ownership

To override an inherited ownership, use the owns keyword with the new owned attribute type, followed by the as keyword and the inherited attribute type:

Override inheritance
define file sub resource, owns file-type as object-type;

The new attribute type that overrides the inherited type is defined in the schema as a subtype of the inherited attribute type. Hence, the inherited attribute type is an abstract type and has the same value type as the new attribute type. The example above in its complete schema form would look like this:

Override inheritance with attributes definitions

path sub attribute, value string;

object-type sub attribute, abstract, value string;
file-type sub object-type, value string;

object sub entity,
    owns object-type;
resource sub object,
file sub resource,
    owns path,
    owns file-type as object-type;

In the above example, the file entity type owns the path and file-type attribute types. The file-type attribute type overrides the inherited object-type attribute type.

Override a role

The label of an inherited role can be overridden to distinguish between roles inherited by a relation subtype versus roles defined by its parent type.

Overriding roles

membership sub relation,
    relates parent,
    relates member;

collection-membership sub membership,
    relates collection as parent;

In the above example, the collection-membership relation type subtypes the membership relation type and overrides the inherited parent role as collection. The inherited member role is inherited as it is.

By overriding an inherited role, we prevent the subtype from relating the role that would otherwise be inherited.


Simple type hierarchy

Simple type hierarchy schema example:


id sub attribute, value long;
email sub attribute, value string;
full-name sub attribute, value string;

user sub entity,
    owns id @key,
    owns email,

person sub user, owns full-name;

The above example defines three attribute types and two entity types. The person entity type is a subtype of the user entity type and inherits ownership of all the attributes owned by user. Unlike user the person type is concrete (not abstract) and can be instantiated (inserted to a database as data).

Provide Feedback