Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: coding conventions #152

Merged
merged 1 commit into from
Mar 7, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
NathanFlurry marked this conversation as resolved.
Show resolved Hide resolved

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 {
}
```
Loading