From b0e7ef890e1796139b5678c28f009542fd23a93f Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Wed, 6 Mar 2024 17:22:00 -0800 Subject: [PATCH] docs: coding conventions --- docs/CONVENTIONS.md | 89 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 12 deletions(-) diff --git a/docs/CONVENTIONS.md b/docs/CONVENTIONS.md index de4b0e37..601881ee 100644 --- a/docs/CONVENTIONS.md +++ b/docs/CONVENTIONS.md @@ -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 { +} +```