Skip to content

Commit 9f2fe47

Browse files
author
Kalagin Ivan
committed
feat: branded types for context, subcontext and features
1 parent 5ba9a6a commit 9f2fe47

4 files changed

Lines changed: 51 additions & 26 deletions

File tree

index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { test } from "uvu";
22
import { snoop } from "snoop";
33
import * as assert from "uvu/assert";
44

5-
import { createError, isConwayError, type IConwayError } from "./index";
5+
import { createError, isConwayError } from "./index";
66

77
test("without error types will throw always UnknownError", () => {
88
const createErrorContext = createError();

index.ts

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,6 @@ type ErrorMap = Record<
100100
}
101101
>;
102102

103-
type FeatureFn<ErrorType extends string> = (
104-
featureName: string,
105-
featureContextExtendedParams?: ExtendedParams
106-
) => CreateErrorFn<ErrorType>;
107-
108103
type ErrorFnOptions = {
109104
originalError?: OriginalError;
110105
extendedParams?: ExtendedParams;
@@ -116,23 +111,34 @@ type CreateErrorFn<ErrorType extends string> = (
116111
options?: ErrorFnOptions
117112
) => IConwayError;
118113

119-
type ErrorSubcontext<ErrorType extends string> = {
114+
type Brand<T, B> = T & { __brand: B };
115+
116+
type ErrorSubcontext<Name extends string, ErrorType extends string> = Brand<Subcontext<Name, ErrorType>, Name>;
117+
type ErrorFeature<Name extends string, ErrorType extends string> = Brand<CreateErrorFn<ErrorType>, Name>;
118+
119+
type Subcontext<Name extends string, ErrorType extends string> = {
120120
/**
121121
* Create a child context within the current context.
122122
*
123123
* @param {string} childContextName - The name of the child context.
124124
* @param {ExtendedParams} extendedParams - Additional extended parameters for the child context.
125125
* @return {Function} Function to create an error context with the specified child context name and extended params.
126126
*/
127-
subcontext: (subcontextName: string, extendedParams?: ExtendedParams) => ErrorSubcontext<ErrorType>;
127+
subcontext: <const ChildContextName extends string>(
128+
subcontextName: ChildContextName,
129+
extendedParams?: ExtendedParams
130+
) => ErrorSubcontext<`${Name}/${ChildContextName}`, ErrorType>;
128131
/**
129132
* Creates a child feature within the current context.
130133
*
131134
* @param {string} childFeatureName - The name of the child feature.
132135
* @param {ExtendedParams} [extendedParams={}] - Additional extended parameters for the child feature.
133136
* @return {Function} The created error feature.
134137
*/
135-
feature: FeatureFn<ErrorType>;
138+
feature: <const FeatureName extends string>(
139+
featureName: FeatureName,
140+
featureContextExtendedParams?: ExtendedParams
141+
) => ErrorFeature<FeatureName, ErrorType>;
136142
};
137143

138144
/**
@@ -146,7 +152,7 @@ export function createError<ErrorTypes extends ErrorTypeConfig>(errorTypes?: Err
146152
const _options = { ...defaultErrorOptions, ...options };
147153
const initialExtendedParams = options?.extendedParams ?? {};
148154

149-
return (contextName: string, extendedParams: ExtendedParams = {}) => {
155+
return <const ContextName extends string>(contextName: ContextName, extendedParams: ExtendedParams = {}) => {
150156
const outerExtendedParams = { ...initialExtendedParams, ...extendedParams };
151157

152158
const errorsMap: ErrorMap = Array.isArray(errorTypes)
@@ -162,30 +168,37 @@ export function createError<ErrorTypes extends ErrorTypeConfig>(errorTypes?: Err
162168
const UnknownError = createErrorClass("UnknownError", contextName);
163169

164170
const _createSubcontext =
165-
(contextName: string, subContextExtendedParams: ExtendedParams) =>
166-
(childContextName: string, extendedParams: ExtendedParams = {}) => {
171+
<const ContextName extends string>(contextName: ContextName, subContextExtendedParams: ExtendedParams) =>
172+
<const ChildContextName extends string>(
173+
childContextName: ChildContextName,
174+
extendedParams: ExtendedParams = {}
175+
) => {
167176
const subErrorContext = { ...subContextExtendedParams, ...extendedParams };
168177
return _createErrorContext(`${contextName}/${childContextName}`, subErrorContext);
169178
};
170179

171-
function _createErrorContext(
172-
_contextName: string,
180+
function _createErrorContext<const ContextName extends string>(
181+
_contextName: ContextName,
173182
contextExtendedParams: ExtendedParams = outerExtendedParams
174-
): ErrorSubcontext<ErrorTypes[number]["errorType"]> {
183+
): ErrorSubcontext<ContextName, ErrorTypes[number]["errorType"]> {
175184
return {
185+
__brand: _contextName,
176186
subcontext: _createSubcontext(_contextName, contextExtendedParams),
177-
feature: (childFeatureName: string, extendedParams: ExtendedParams = {}) => {
187+
feature: <const FeatureName extends string>(
188+
childFeatureName: FeatureName,
189+
extendedParams: ExtendedParams = {}
190+
) => {
178191
const featureErrorContext = { ...contextExtendedParams, ...extendedParams };
179192
return _createErrorFeature(childFeatureName, _contextName, featureErrorContext);
180193
},
181194
};
182195
}
183196

184-
function _createErrorFeature(
185-
featureName: string,
197+
function _createErrorFeature<const FeatureName extends string>(
198+
featureName: FeatureName,
186199
contextName: string,
187200
featureContextExtendedParams: ExtendedParams = {}
188-
) {
201+
): ErrorFeature<FeatureName, ErrorTypes[number]["errorType"]> {
189202
const createNewErrorObject: CreateErrorFn<ErrorTypes[number]["errorType"]> = (
190203
errorType,
191204
message: string,
@@ -215,7 +228,8 @@ export function createError<ErrorTypes extends ErrorTypeConfig>(errorTypes?: Err
215228
return error;
216229
};
217230

218-
return createNewErrorObject;
231+
Object.assign(createNewErrorObject, { __brand: featureName });
232+
return createNewErrorObject as ErrorFeature<FeatureName, ErrorTypes[number]["errorType"]>;
219233
}
220234

221235
return _createErrorContext(contextName);

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"name": "conway-errors",
33
"source": "index.ts",
4-
"version": "3.1.2",
4+
"version": "3.2.0",
55
"private": false,
6-
"description": "Create errors with Conway's law",
6+
"description": "Simplify the creation, structuring and throwing of errors",
77
"exports": {
88
"types": "./dist/index.d.ts",
99
"require": "./dist/index.js",
@@ -31,8 +31,19 @@
3131
"/dist",
3232
"/package.json"
3333
],
34-
"keywords": [],
35-
"author": "Kalagin Ivan",
34+
"keywords": [
35+
"errors",
36+
"typescript",
37+
"error-handling",
38+
"custom-errors",
39+
"structured-errors",
40+
"error-utils",
41+
"backend",
42+
"exceptions",
43+
"js-errors",
44+
"application-error"
45+
],
46+
"author": "ivklgn",
3647
"license": "MIT",
3748
"devDependencies": {
3849
"@biomejs/biome": "1.8.3",

0 commit comments

Comments
 (0)