Modeling Schema.org JSON-LD in TypeScript: A Story in Four Parts
Tags: schemaorg, typescript, javascript, jsonld
Published: 2019-07-09 04:33:15

---
title: Modeling Schema.org JSON-LD in TypeScript: A Story in Four Parts
published: true
description: some TypeScript type system features, such as discriminated type unions, powerful type inference, nullability checking, and type intersections, present an opportunity to both model what Schema.org-conformant JSON-LD looks like, while also providing ergonomic completions to the developer
tags: schemaorg, typescript, javascript, json-ld
cover_image: https://i0.wp.com/blog.eyas.sh/wp-content/uploads/2019/05/article-cover.png
---

Recently, I published [**schema-dts**](https://dev.to/eyassh/schema-dts-schemaorg-linked-data-types--code-generation-544p), an open source library that models JSON-LD Schema.org in TypeScript. A big reason I wanted to do this project is because I knew some TypeScript type system features, such as discriminated type unions, powerful type inference, nullability checking, and type intersections, present an opportunity to both model what Schema.org-conformant JSON-LD looks like, while also providing ergonomic completions to the developer.

I wrote a series of posts, describing some of the Structured Data concepts that lent themselves well to TypeScript’s type system, and those concepts that didn’t.


## 1. [Modeling Schema.org Class Hierarchy using Structural Typing](https://blog.eyas.sh/2019/05/modeling-schema-org-schema-with-typescript-the-power-and-limitations-of-the-typescript-type-system/)

Schema.org JSON-LD node objects are always *typed* (that is, they have a `@type` property that points to some IRI–a string–describing it). Given a `@type` you know all the properties that are defined on a particular object. Object types inherit from each other. For example, `Thing` in Schema.org has a property called `name`, and `Person` is a subclass of `Thing` that defines additional properties such as `birthDate`, and inherits all the properties of `Thing` such as `name`. `Thing` has other sub-classes, like `Organization`, with it’s own properties, like `logo`.

In the [**first installment**](https://blog.eyas.sh/2019/05/modeling-schema-org-schema-with-typescript-the-power-and-limitations-of-the-typescript-type-system/), we end up uncovering a recursive TypeScript inheritance hierarchy we can use to model the full complexity of Schema.org class inheritance.

![Recursive Hierarchy](https://i1.wp.com/blog.eyas.sh/wp-content/uploads/2019/05/type-system-recursive.png)

## 2. [Schema.org Enumerations in TypeScript](https://blog.eyas.sh/2019/05/schema-org-enumerations-in-typescript/)

When trying to model Enumerations, we looked at a ton of examples from the Schema.org website to discover that [absolute IRIs](https://www.w3.org/TR/json-ld/#iris) or `@context`-relative IRIs are expected to model the *value* of an enumeration. But we also found out that Enumerations can be arbitrary nodes, and take part in the class hierarchy.

![Example of an Enum Hierarchy](https://i2.wp.com/blog.eyas.sh/wp-content/uploads/2019/05/audience-schema-1.png)

## 3. [Schema.org DataType in TypeScript](https://blog.eyas.sh/2019/07/schema-org-datatype-in-typescript-structural-typing-doesnt-cut-it/)

The Schema.org DataType hierarchy is far richer than TypeScript’s type system can accommodate. In [the third installment](https://blog.eyas.sh/2019/07/schema-org-datatype-in-typescript-structural-typing-doesnt-cut-it/), we figured out what trade-offs can we make.


## 4. [Class Properties and Special Cases](https://blog.eyas.sh/2019/07/schema-org-classes-in-typescript-properties-and-special-cases/)

Properties -- all the *stuff* that actually lives within a JSON node -- turn out to be more complicated than we thought: they're all optional, they're all repeated, they can supersede one another, and then can subclass one another.

# The End Result

The end result is **schema-dts** itself. We can create programmatic TypeScript definitions that express much of Schema.org. For example, the top-level `Thing` type in Schema.org can be represented as:

```ts
type ThingBase = {
    /** An additional type for the item, typically used for adding more specific types from external vocabularies in microdata syntax. This is a relationship between something and a class that the thing is in. In RDFa syntax, it is better to use the native RDFa syntax - the 'typeof' attribute - for multiple types. Schema.org tools may have only weaker understanding of extra types, in particular those defined externally. */
    "additionalType"?: URL | URL[];
    /** An alias for the item. */
    "alternateName"?: Text | Text[];
    /** A description of the item. */
    "description"?: Text | Text[];
    /** A sub property of description. A short description of the item used to disambiguate from other, similar items. Information from other properties (in particular, name) may be necessary for the description to be useful for disambiguation. */
    "disambiguatingDescription"?: Text | Text[];
    /** The identifier property represents any kind of identifier for any kind of {@link http://schema.org/Thing Thing}, such as ISBNs, GTIN codes, UUIDs etc. Schema.org provides dedicated properties for representing many of these, either as textual strings or as URL (URI) links. See {@link /docs/datamodel.html#identifierBg background notes} for more details. */
    "identifier"?: (PropertyValue | Text | URL) | (PropertyValue | Text | URL)[];
    /** An image of the item. This can be a {@link http://schema.org/URL URL} or a fully described {@link http://schema.org/ImageObject ImageObject}. */
    "image"?: (ImageObject | URL) | (ImageObject | URL)[];
    /** Indicates a page (or other CreativeWork) for which this thing is the main entity being described. See {@link /docs/datamodel.html#mainEntityBackground background notes} for details. */
    "mainEntityOfPage"?: (CreativeWork | URL) | (CreativeWork | URL)[];
    /** The name of the item. */
    "name"?: Text | Text[];
    /** Indicates a potential Action, which describes an idealized action in which this thing would play an 'object' role. */
    "potentialAction"?: Action | Action[];
    /** URL of a reference Web page that unambiguously indicates the item's identity. E.g. the URL of the item's Wikipedia page, Wikidata entry, or official website. */
    "sameAs"?: URL | URL[];
    /** A CreativeWork or Event about this Thing.. */
    "subjectOf"?: (CreativeWork | Event) | (CreativeWork | Event)[];
    /** URL of the item. */
    "url"?: URL | URL[];
};
/** The most generic type of item. */
export type Thing = ({
    "@type": "Thing";
} & ThingBase) | (Action | CreativeWork | Event | Intangible | MedicalEntity | Organization | Person | Place | Product);

```

View the entire series at **https://blog.eyas.sh/tag/schema.org**