# Lesson 5.2: Defining type hierarchies

## [](#_defining_entity_type_hierarchies)Defining entity type hierarchies

To define type hierarchies, we use the same Define queries as when defining individual types. The following query shows how we can define a hierarchy of book types.

```typeql
define
entity book;
entity paperback, sub book;
entity hardback, sub book;
entity ebook, sub book;
```

_→ Commit_

To make one type a subtype of another, we simply add a `sub` statement with the intended **supertype** to the kind statement that defines that type. When defined in a type hierarchy, subtypes will **inherit** all the owned attributes and played roles of their supertypes. Consider the following Define query where we assign a number of ownerships and roles to `book`.

```typeql
define
entity book owns title,
  owns page-count,
  owns genre,
  owns price,
  plays publishing:published,
  plays order-line:item,
  plays rating:rated,
  plays promotion-inclusion:item;
attribute title, value string;
attribute page-count, value integer;
attribute genre, value string;
```

_→ Commit_

`paperback`, `hardback`, and `ebook` will inherit all of these capabilities from `book` without having to explicitly declare this to be the case. This will even apply to new subtypes of `book` defined in the future! If we want capabilities that only apply to certain subtypes, then we can declare them directly on those types only.

```typeql
define
entity paperback, owns stock;
entity hardback, owns stock;
attribute stock, value integer;
```

_→ Commit_

This allows paperbacks and hardbacks to have stock levels, but not ebooks.

Exercise

Write a query to define an entity type `company` with two subtypes `publisher` and `courier`, and define the following capabilities:

*   All companies can own attributes of the existing type `name`.
    
*   Publishers can also play the existing role `publishing:publisher`.
    
*   Couriers can also play the existing role `delivery:deliverer`.
    

Sample solution

```typeql
define
entity company,
  owns name;
entity publisher,
  sub company,
  plays publishing:publisher;
entity courier,
  sub company,
  plays delivery:deliverer;
```

_→ Commit_

Every type has exactly one supertype: TypeDB supports single inheritance only, so a type cannot be given two parents. This is not restrictive in practice. When a type seems to need a second parent, it is usually because one of the two hierarchies describes a **behaviour** or **capability** of the type rather than what it inherently _is_, and behaviours are better modeled as interfaces: roles and attribute ownerships can be implemented by any number of otherwise unrelated types, giving each type as many capabilities as needed. This use of composition instead of multiple inheritance is explored in the [schema modeling guide](../../../guides/schema-modeling/index.md#_subtyping_vs_composition).

## [](#_defining_abstract_types)Defining abstract types

Abstract types cannot be directly instantiated, much like abstract classes in OOP. An abstract type can only be instantiated via its subtypes. To make a type abstract, we use an `@abstract` annotation. The following Define query makes the existing types `book` and `company` abstract.

```typeql
define
entity book @abstract;
entity company @abstract;
```

_→ Commit_

Now `book` can only be instantiated through `paperback`, `hardback`, or `ebook`, and `company` can only be instantiated through `publisher` or `courier`.

Exercise

Write a query to define an abstract entity type `place` with three subtypes, `country`, `state`, and `city`, and define the following capabilities:

*   All places can own attributes of the existing type `name`.
    
*   All places can play the existing role `locating:location`.
    
*   All places can play the existing role `locating:located`.
    

Sample solution

```typeql
define
entity place @abstract,
    owns name,
    plays locating:location,
    plays locating:located;
entity country, sub place;
entity state, sub place;
entity city, sub place;
```

_→ Commit_

## [](#_defining_relation_type_hierarchies)Defining relation type hierarchies

Relation type hierarchies are defined in the same way as entity type hierarchies. When defined in a hierarchy, subtypes of a relation type inherit all its roles. The following Define query defines a relation type `contribution` and three subtypes.

```typeql
define
relation contribution,
    relates work,
    relates contributor;
relation authoring, sub contribution;
relation editing, sub contribution;
relation illustrating, sub contribution;
```

_→ Commit_

Here, `authoring`, `editing`, and `illustrating` inherit the roles `work` and `contributor` from `contribution`. This would allow instantiation of the following relation types:

*   `contribution` with roles `contribution:work` and `contribution:contributor`
    
*   `authoring` with roles `contribution:work` and `contribution:contributor`
    
*   `editing` with roles `contribution:work` and `contribution:contributor`
    
*   `illustrating` with roles `contribution:work` and `contribution:contributor`
    

We could also define new roles on a specific subtype using the `relates` keyword on that subtype, in the same way we can define capabilities on specific subtypes using the `owns` and `plays` keywords on them. A new role can be made to **override** an existing role of a parent relation using the `as` keyword. In the following query, the roles `author`, `editor`, and `illustrator` override the inherited `contributor` role.

```typeql
define
relation authoring, relates author as contributor;
relation editing, relates editor as contributor;
relation illustrating, relates illustrator as contributor;
```

_→ Commit_

This would instead allow instantiation of the following relation types:

*   `contribution` with roles `contribution:work` and `contribution:contributor`
    
*   `authoring` with roles `contribution:work` and `authoring:author`
    
*   `editing` with roles `contribution:work` and `editing:editor`
    
*   `illustrating` with roles `contribution:work` and `illustrating:illustrator`
    

An **overridden role** defines a subtype of the role, and makes the role abstract in the sub-relation. In other words, you get a new role that can be used in place of the parent role, and you cannot use the parent role in the sub relation.

Exercise

Write a query to define the following new role players:

*   The existing type `book` playing `contribution:work`.
    
*   The existing type `contributor` playing `contribution:contributor` and its three subtypes.
    

Sample solution

```typeql
define
entity book, plays contribution:work;
entity contributor, plays contribution:contributor;
entity contributor, plays authoring:author;
entity contributor, plays editing:editor;
entity contributor, plays illustrating:illustrator;
```

_→ Commit_

With this schema, it is no longer possible to instantiate `authoring`, `editing`, and `illustrating` with the `contribution:contributor` role. However, it is still possible to query them via that role, as the overriding roles `authoring:author`, `editing:editor`, and `illustrating:illustrator` are considered to be its **subtypes**. The `match` clause of the following Fetch query would match instances of `contributor`, `authoring`, `editing`, and `illustrating`, and contributor names would be returned for all contributor roles.

```typeql
match
contribution (work: $book, contributor: $contributor);
fetch {
  "title": $book.title,
  "name": $contributor.name
};
```

Exercise

Modify the above Fetch query to retrieve only author names instead of all contributor names.

Sample solution

```typeql
match
authoring (work: $book, author: $contributor);
fetch {
  "title": $book.title,
  "name": $contributor.name,
};
```

Now instead modify it to retrieve only names of contributors who have made miscellaneous contributions (i.e. those in direct instances of `contribution` and not its subtypes).

Sample solution

```typeql
match
$rel isa! contribution, links (work: $book, contributor: $contributor);
fetch {
  "title": $book.title,
  "name": $contributor.name,
};
```

Notice we have used the `isa!` keyword.

## [](#_defining_attribute_type_hierarchies)Defining attribute type hierarchies

Attribute types can also be defined hierarchically.

```typeql
define
attribute isbn value string;
attribute isbn-13 sub isbn;
attribute isbn-10 sub isbn;
```

_→ Commit_

Value types of attribute types are inherited by their subtypes. In this case, `isbn-13` and `isbn-10` inherit the `string` value type.

If you want to omit the value type in the supertype, to allow different value types in its subtypes, you must make the supertype abstract:

```typeql
define
attribute id @abstract;
attribute email-id, sub id, value string;
attribute numerical-id, sub id, value integer;
```

[Defining individual types](../5.1-defining-individual-types/index.md) [Defining constraints](../5.3-defining-constraints/index.md)

[Edit on GitHub](https://github.com/typedb/typedb-docs/edit/3.x-development/academy/modules/ROOT/pages/5-defining-schemas/5.2-defining-type-hierarchies.adoc) Edit this page on GitHub.