Skip to content

Commit

Permalink
docs: coding conventions
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanFlurry committed Mar 7, 2024
1 parent 9950b74 commit b0e7ef8
Showing 1 changed file with 77 additions and 12 deletions.
89 changes: 77 additions & 12 deletions docs/CONVENTIONS.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,94 @@
# TypeScript Conventions

## Use interfaces & functions, not classes

Use exclusively interfaces & standalone functions internally. This keeps code
clean, legible, and easy to refactor at the cost of slightly more verbosity.

Examples: `Project`, `Registry`, `Module`, `Script`

## Only use classes for developer-facing APIs

We don't expect the user to use pure interfaces & functions; consuming well
designed OOP interfaces can be cleaner.

Examples of using classes: `Context` and `Runtime` are classes designed to have
a clean interface for the user.

Examples of using interfaces: `Trace` is a simple data container that can be
easily serialized & deserialized. There are standalone functions that can
operate on a trace.

## Camel case + acryonyms

OpenApi vs OpenAPI Uuid vs UUID
Follow camel case strictly & treat acronyms as single words.

Examples:

TODO
- Prefer `OpenApi` insetad of `OpenAPI`
- Prefer `Uuid` instead of `UUID`

## Uses of `id` included with type

`id` vs `userId`
When referring to the ID of the current type, use `id`. When referring to a
foreign type, use `{type name}Id`.

Example:

```prisma
model User {
id String @id @default(uuid()) @db.Uuid
posts Post[]
}
TODO
model Post {
id String @id @default(uuid()) @db.Uuid
userId String @db.Uuid
user User @relation(fields: [userId], references: [id])
}
```

## Externally tagged enums

Easy to represent in OpenAPI & external languages compared to internally tagged
enums
When representing an enum with associated data (often called "sum types" which are a kind of algebreic data type, ADT), represent using a nested object (often called "externally tagged enums").

This comes at the expense of not having exhaustive switch statements.

Externally tagged enums are easy to represent in languages that don't support advanced type constraints, such as C# and most OpenAPI SDK generators (i.e. don't support `oneOf`).

This example:

```typescript
type MyEnum = { foo: MyEnumFoo } | { bar: MyEnumBar } | { baz: MyEnumBaz };

interface MyEnumFoo {

}

interface MyEnumBar {

}

interface MyEnumBaz {

}
```

Comes at the down side of not having exhaustive switch statements
Can be represented in C# like this:

## Avoid OOP at all costs
```cs
class MyEnum {
MyEnumFoo? Foo;
MyEnumBar? Bar;
MyEnumBaz? Baz;
}

## Prefer interfaces for rich data, prefer classes for functional objects
class MyEnumFoo {
}

`Trace` simply stores data. Helper functions can be published to manage traces.
class MyEnumBar {
}

`Context` and `Runtime` are functional classes that have associated methods &
constructors.
class MyEnumBaz {
}
```

0 comments on commit b0e7ef8

Please sign in to comment.