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

Use type-fest's Tagged type for simple nominal types #8757

Merged
merged 1 commit into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions src/types/modComponentTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ export type HydratedModComponent<Config extends UnknownObject = UnknownObject> =
/**
* Brand for nominal typing.
*/
// XXX: defining our own brand vs. using type-fest's tagged type because we need to be able to apply the brand
// in cooky-cutter factory definitions
_hydratedModComponentBrand: never;
};

Expand Down
16 changes: 4 additions & 12 deletions src/types/registryTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,12 @@

import { type UUID } from "@/types/stringTypes";
import { type ApiVersion } from "@/types/runtimeTypes";
import { type ValueOf } from "type-fest";
import { type Tagged, type ValueOf } from "type-fest";

/**
* A brick registry id conforming to `@scope/collection/name`
*/
export type RegistryId = string & {
// Nominal subtyping
_registryIdBrand: never;
};
export type RegistryId = Tagged<string, "RegistryId">;

/**
* Scope for inner definitions
Expand Down Expand Up @@ -53,9 +50,7 @@ export type DefinitionKind = ValueOf<typeof DefinitionKinds>;
/**
* Simple semantic version number, major.minor.patch
*/
export type SemVerString = string & {
_semVerBrand: never;
};
export type SemVerString = Tagged<string, "SemVer">;

/**
* Metadata about a Brick, StarterBrick, Integration, or Mod.
Expand Down Expand Up @@ -117,10 +112,7 @@ export type InnerDefinitions = Record<string, UnknownObject>;
* A reference to an entry in the mod's `definitions` map. _Not a valid RegistryId_.
* @see InnerDefinitions
*/
export type InnerDefinitionRef = string & {
// Nominal subtyping
_innerDefinitionRefBrand: never;
};
export type InnerDefinitionRef = Tagged<string, "InnerDefinitionRef">;

export interface RegistryItem<T extends RegistryId = RegistryId> {
id: T;
Expand Down
40 changes: 17 additions & 23 deletions src/types/runtimeTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import { type ComponentType } from "react";
import { type SafeHTML, type UUID } from "@/types/stringTypes";
import { type SanitizedIntegrationConfig } from "@/integrations/integrationTypes";
import { type Primitive } from "type-fest";
import { type Primitive, type Tagged } from "type-fest";
import { type Logger } from "@/types/loggerTypes";
import { type BrickPipeline } from "@/bricks/types";
import { type PanelPayload } from "./sidebarTypes";
Expand Down Expand Up @@ -54,9 +54,7 @@ export function isDocument(root: SelectorRoot): root is Document {
* A reference to an element on the page.
* @see getReferenceForElement
*/
export type ElementReference = UUID & {
_elementReferenceBrand: never;
};
export type ElementReference = Tagged<UUID, "ElementReference">;

/**
* A reference to a React component produced by a Renderer brick.
Expand All @@ -75,17 +73,15 @@ export type RendererOutput = SafeHTML | ComponentRef;
/**
* A valid identifier for a brick output key or a service key. (Does not include the preceding "@".)
*/
export type OutputKey = string & {
_outputKeyBrand: never;
};
export type OutputKey = Tagged<string, "OutputKey">;

/**
* A variable with a "@"-prefix that refers to an integration
*/
export type IntegrationDependencyVarRef = string & {
// Preserve legacy branding field name for backwards compatibility
_serviceVarRefBrand: never;
};
export type IntegrationDependencyVarRef = Tagged<
string,
"IntegrationDependencyVarRef"
>;

/**
* A text template engine.
Expand Down Expand Up @@ -226,12 +222,13 @@ export type OptionsArgs = Record<string, Primitive>;
* @see RenderedArgs
* @see BrickConfig.outputKey
*/
export type BrickArgsContext = UnknownObject & {
// Nominal typing
_blockArgsContextBrand: never;
"@input": UnknownObject;
"@options"?: OptionsArgs;
};
export type BrickArgsContext = Tagged<
UnknownObject & {
"@input": UnknownObject;
"@options"?: OptionsArgs;
},
"BrickArgsContext"
>;

/**
* Returns an object as a BrickArgsContext, or throw a TypeError if it's not a valid context.
Expand All @@ -257,19 +254,16 @@ export function validateBrickArgsContext(obj: UnknownObject): BrickArgsContext {
export type BrickArgs<
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- brick is responsible for providing shape
T extends Record<string, any> = Record<string, any>,
> = T & {
_blockArgBrand: never;
};
> = Tagged<T, "BrickArgs">;

/**
* The non-validated arguments to pass into the `run` method of a Brick.
* @see BrickArgs
*/
export type RenderedArgs = UnknownObject & {
_renderedArgBrand: never;
};
export type RenderedArgs = Tagged<UnknownObject, "RenderedArgs">;

export type IntegrationsContextValue = {
// NOTE: this is not a nominal type brand. The `__service` key is actually used in the runtime.
__service: SanitizedIntegrationConfig;
[prop: string]: string | SanitizedIntegrationConfig | null;
};
Expand Down
32 changes: 8 additions & 24 deletions src/types/stringTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { type Tagged } from "type-fest";

export const VALID_UUID_REGEX =
/^[\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[1-5][\dA-Fa-f]{3}-[89ABab][\dA-Fa-f]{3}-[\dA-Fa-f]{12}$/;

Expand All @@ -23,49 +25,31 @@ export const VALID_UUID_REGEX =
* @see uuidv4
* @see isUUID
*/
export type UUID = string & {
// Nominal subtyping
_uuidBrand: never;
};
export type UUID = Tagged<string, "UUID">;

/**
* An ISO timestamp string
*/
export type Timestamp = string & {
// Nominal subtyping
_uuidTimestamp: never;
};
export type Timestamp = Tagged<string, "Timestamp">;

/**
* Base64 encoded JSON string
*/
export type EncodedJSON = string & {
// Nominal subtyping
_encodedJSONBrand: never;
};
export type EncodedJSON = Tagged<string, "EncodedJSON">;

/**
* A UTC timestamp followed by a sequence number valid in the current context.
* Useful to determine order of two calls to getTimedSequence.
*/
export type TimedSequence = string & {
// Nominal subtyping
_timedSequence: never;
};
export type TimedSequence = Tagged<string, "TimeSequence">;

/**
* A string known not to be tainted with user-generated input.
*/
export type SafeString = string & {
// Nominal subtyping
_safeStringBrand: never;
};
export type SafeString = Tagged<string, "SafeString">;

/**
* Rendered HTML that has been sanitized.
* @see sanitize
*/
export type SafeHTML = string & {
// Nominal subtyping
_safeHTMLBrand: never;
};
export type SafeHTML = Tagged<string, "SafeHTML">;
Loading