From f6af7158ca33d38185c87806adf1f84827bfe502 Mon Sep 17 00:00:00 2001 From: Erik Marks <25517051+rekmarks@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:37:20 -0700 Subject: [PATCH 1/2] docs: Add section on type-only dependencies --- docs/typescript.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/typescript.md b/docs/typescript.md index 4ea228ec..2b57f09c 100644 --- a/docs/typescript.md +++ b/docs/typescript.md @@ -881,9 +881,16 @@ export class ComposableController< const controllerMessenger = ControllerMessenger; ``` +### Type-Only Dependencies + +If package `a` imports only types from `b`, `b` be a dev or production dependency of `a`? +This depends on whether types from `b` are imported in the published `.d.ts` files of `a`. +This occurs if e.g. `a` exports a function that uses a type from `b` in its signature. +See the [TypeScript handbook](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#dependencies) on this topic for more details. + ## Generic Types -#### Constrain generic types if necessary +### Constrain generic types if necessary It may not be enough just to have a type or a function take another type — you might have to constrain it if it's not allowed to be anything (e.g. extends Json) @@ -897,7 +904,7 @@ function createExampleMiddleware< >(exampleParam); ``` -#### Use `Omit` to reduce requirements +### Use `Omit` to reduce requirements `Omit` takes two generic types: `T` representing the original object type and `K` representing the property keys you want to remove. It returns a new type that has all the properties of T except for the ones specified in K. Here are some cases to use omit: @@ -936,7 +943,7 @@ const cartPayload: singleItemPayload[] = []; ## Interfaces -#### Always prefer type aliases over the `interface` keyword +### Always prefer type aliases over the `interface` keyword We enforce consistent and exclusive usage of type aliases over the `interface` keyword to declare types for several reasons: @@ -946,7 +953,7 @@ We enforce consistent and exclusive usage of type aliases over the `interface` k - Unlike interfaces, type aliases extend `Record` and have an index signature of `string` by default, which makes them compatible with our Json-serializable types (most notably `Record`). - Type aliases can be freely merged using the intersection (`&`) operator, like interfaces which can implement multiple inheritance. -#### `implements` keyword +### `implements` keyword The `implements` keyword enables us to define and enforce interfaces, i.e. strict contracts consisting of expected object and class properties and abstract method signatures. Writing an interface to establish the specifications of a class that external code can interact while without being aware of internal implementation details is encouraged as sound OOP development practice. @@ -974,7 +981,7 @@ The concept of the interface as discussed in this section is not to be confused TypeScript offers several tools for crafting clear data definitions, with enumerations and unions standing as popular choices. -#### Consider using enums over union types for situations with a fixed set of known values. +### Consider using enums over union types for situations with a fixed set of known values. Inevitably you will want to refer to the values of a union type somewhere (perhaps as the argument to a function). You can of course just use a literal which represents a member of that union — but if you have an enum, then all of the values are special, and any time you use a value then anyone can see where that value comes from. @@ -994,7 +1001,7 @@ enum AccountType { } ``` -#### Don't use numeric enums +### Don't use numeric enums Numeric enums are misleading because it creates a reverse mapping from value to property name, and when using `Object.values` to access member names, it will return the numerical values instead of the member names, potentially causing unexpected behavior. 🚫 @@ -1025,7 +1032,7 @@ const directions = Object.values(Direction); // ["Up", "Down", "Left", "Right"] ## Functions -#### For functions and methods, provide explicit return types +### For functions and methods, provide explicit return types Although TypeScript is capable of inferring return types, adding them explicitly makes it much easier for the reader to see the API from the code alone and prevents unexpected changes to the API from emerging. @@ -1065,7 +1072,7 @@ async function removeAccount(address: Hex): Promise { } ``` -#### For selector functions, define the input state argument with the narrowest type that preserves functionality +### For selector functions, define the input state argument with the narrowest type that preserves functionality A selector function that directly queries state properties should define its input state argument as a subtype of root state that only contains the required queried properties. From 5822978c1ffeed47f8468029a706e3107b629f7d Mon Sep 17 00:00:00 2001 From: Erik Marks <25517051+rekmarks@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:23:59 -0700 Subject: [PATCH 2/2] Fix typo in docs/typescript.md Co-authored-by: Elliot Winkler --- docs/typescript.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/typescript.md b/docs/typescript.md index 2b57f09c..8435cae9 100644 --- a/docs/typescript.md +++ b/docs/typescript.md @@ -883,7 +883,7 @@ export class ComposableController< ### Type-Only Dependencies -If package `a` imports only types from `b`, `b` be a dev or production dependency of `a`? +If package `a` imports only types from `b`, should `b` be a dev or production dependency of `a`? This depends on whether types from `b` are imported in the published `.d.ts` files of `a`. This occurs if e.g. `a` exports a function that uses a type from `b` in its signature. See the [TypeScript handbook](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#dependencies) on this topic for more details.