Entities
This project and documentation are work in progress
Entities are where we encode the types in our system. You might call these 'models' or 'domain objects'. We also store types related to entities, such as the objects used to create / update them.
It's time for our first proper look at the example code. Open the entities folder and you'll see encodings for all the types required by our example system - a system for a "community library", with users, books and loans:
- Book.ts
- Loan.ts
- User.ts
- cast.ts
- index.ts
Let's dive right into the Book type:
import * as t from 'io-ts'
import { UUID } from 'io-ts-types/lib/UUID'
import { $cast } from './cast'
// IO-TS Codecs
// ---------------------------------------------------------------
export const BookInput = t.exact(t.type({
name: t.string
}))
export const Book = t.exact(
t.type({
id: UUID,
name: t.string
})
)
// Casts
// ---------------------------------------------------------------
export const castBook = $cast(Book)
export const castBookInput = $cast(BookInput)
// Static types
// ---------------------------------------------------------------
export type Book = t.TypeOf<typeof Book>
export type BookInput = t.TypeOf<typeof BookInput>
There's a lot going on here. Let's break the code down:
What is io-ts and all this t.type business?
IO-TS is a library for defining types that can be verified at runtime, which it calls 'codecs'. You can derive TypeScript types from them too, meaning that you can now 'prove' your TypeScript types are an accurate representation of the data being received.
Naturally, this makes your project much more robust, because you know real-world data won't sneak past your type system
Codecs are pretty easy to write and map quite transparently to TypeScript types.
For instance, if you have a codec like this:
const Person = t.type({
name: t.string,
age: t.number
})
You can derive a typescript static type:
type Person = t.TypeOf<typeof Person>
where type Person equivalent to {
name: string,
age: number
}
But you can also derive a runtime check. Here I'm using a util $cast
that's defined in my project:
const castPerson = $cast(Person)
const returns_a_person = castPerson({
name: 'Ned Flanders',
age: 56
}) // OK!
const throws_an_error = castPerson({
age: 100
}) // Throws an error that 'name:string' is missing!
You can find the $cast
helper in cast.ts
Do I have to use io-ts? What if I just want to use plain JS?
Not at all - you can use plain TypeScript types
and interfaces
if you prefer. That said, being able to validate your types is a huge boost to your ability to clamp down on bugs and unpredictable behaviour, so I'd recommend giving it a go. There are also libraries like joi you can use.
If you're using plain JS and don't care about validation, you might just write types in JSDoc or even skip the entities folder entirely (though this will make working with operations a little harder, as the types will be less obvious).
Back to the book types
Now we know what IO-TS does, we can deconstruct the Book.ts
module:
There's a Book, which obviously encodes a book a user can borrow
There's a BookInput, which is used during Book CRUD
There are casts for both of the above 'codecs'
There are static types derived from both codecs
The index.ts file
At the root of the entities folder should be an index.ts
file that exports all the static types and casts defined within each module. This will itself then be exported at the library root so that adapters and bindings can import domain types.
Last updated
Was this helpful?