Skip to content

Commit 3bb3396

Browse files
committed
feat(toolkit-type-helpers): ✨ add type utilities for writeable and union discriminators, and restructure tsconfig for better type management
1 parent c34be36 commit 3bb3396

File tree

15 files changed

+140
-64
lines changed

15 files changed

+140
-64
lines changed

.qodo/history.sqlite

0 Bytes
Binary file not shown.

packages/toolkit-core/package.json

+2-4
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020
"hooks"
2121
],
2222
"sideEffects": false,
23-
"exports": {
24-
".": "./dist/esm/index.js"
25-
},
23+
"exports": "./dist/esm/index.js",
2624
"main": "./dist/esm/index.js",
2725
"module": "./dist/esm/index.js",
2826
"types": "./dist/esm/index.d.ts",
@@ -40,7 +38,7 @@
4038
"lint:attw": "attw --pack . --ignore-rules=cjs-resolves-to-esm",
4139
"lint:publint": "publint --strict .",
4240
"lint:size": "size-limit",
43-
"lint:type-check": "tsc --pretty --incremental -p tsconfig.json",
41+
"lint:type-check": "tsc --pretty -p tsconfig.json",
4442
"release": "pnpm publish --no-git-checks",
4543
"release:test": "pnpx pkg-pr-new publish"
4644
},

packages/toolkit-core/tsconfig.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
{
2-
"extends": "@zayne-labs/tsconfig/bundler/dom/library",
2+
"extends": ["../../tsconfig.base.json", "@zayne-labs/tsconfig/bundler/dom/library"],
33

44
"compilerOptions": {
55
"baseUrl": ".",
66
"paths": {
77
"@/*": ["src/*"]
88
}
9-
// "declaration": true,
10-
// "isolatedDeclarations": true
119
},
1210

1311
"include": ["src/**/*", "**/*"],

packages/toolkit-react/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"lint:attw": "attw --pack . --ignore-rules=cjs-resolves-to-esm",
4343
"lint:publint": "publint --strict .",
4444
"lint:size": "size-limit",
45-
"lint:type-check": "tsc --pretty --incremental -p tsconfig.json",
45+
"lint:type-check": "tsc --pretty -p tsconfig.json",
4646
"release": "pnpm publish --no-git-checks",
4747
"release:test": "pnpx pkg-pr-new publish"
4848
},

packages/toolkit-react/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends": "@zayne-labs/tsconfig/bundler/dom/library",
2+
"extends": ["../../tsconfig.base.json", "@zayne-labs/tsconfig/bundler/dom/library"],
33

44
"compilerOptions": {
55
"baseUrl": ".",

packages/toolkit-type-helpers/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"lint:attw": "attw --pack . --ignore-rules=cjs-resolves-to-esm",
4141
"lint:publint": "publint --strict .",
4242
"lint:size": "size-limit",
43-
"lint:type-check": "tsc --pretty --incremental -p tsconfig.json",
43+
"lint:type-check": "tsc --pretty -p tsconfig.json",
4444
"release": "pnpm publish --no-git-checks",
4545
"release:test": "pnpx pkg-pr-new publish",
4646
"test": "vitest run",

packages/toolkit-type-helpers/src/guard.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import type { AnyAsyncFunction, AnyFunction, UnknownObject, UnknownObjectWithAnyKey } from "./type-utils";
1+
import type {
2+
AnyAsyncFunction,
3+
AnyFunction,
4+
UnknownObject,
5+
UnknownObjectWithAnyKey,
6+
} from "./type-utils/common";
27

38
export const isString = (value: unknown) => typeof value === "string";
49

packages/toolkit-type-helpers/src/type-utils.ts packages/toolkit-type-helpers/src/type-utils/common.ts

+2-50
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
/* == The intersection with "{}" or "unknown" or "NonNullable<unknown>" is necessary to make it work as expected based on quirks in the TS compiler */
1+
// == The intersection with "{}" or "unknown" or "NonNullable<unknown>" is necessary to make it work as expected based on quirks in the TS compiler
22
export type Prettify<TObject> = NonNullable<unknown> & { [Key in keyof TObject]: TObject[Key] };
33

4-
/* == Using this Immediately Indexed Mapped type helper to help show computed type of anything passed to it instead of just the vague type name */
4+
// == Using this Immediately Indexed Mapped type helper to help show computed type of anything passed to it instead of just the vague type name
55
export type UnmaskType<TType> = { _: TType }["_"];
66

77
export type PrettyOmit<TObject, Key extends keyof TObject> = Prettify<Omit<TObject, Key>>;
@@ -16,54 +16,6 @@ export type SelectorFn<TStore, TResult> = (state: TStore) => TResult;
1616
// ? TEnum[number]
1717
// : TEnum[keyof TEnum];
1818

19-
export type WriteableVariantUnion = "deep" | "shallow";
20-
21-
/**
22-
* Makes all properties in an object type writeable (removes readonly modifiers).
23-
* Supports both shallow and deep modes, and handles special cases like arrays, tuples, and unions.
24-
* @template TObject - The object type to make writeable
25-
* @template TVariant - The level of writeable transformation ("shallow" | "deep")
26-
*/
27-
export type Writeable<
28-
TObject,
29-
TVariant extends WriteableVariantUnion = "shallow",
30-
> = TObject extends readonly [...infer TTupleItems]
31-
? TVariant extends "deep"
32-
? [
33-
...{
34-
[Key in keyof TTupleItems]: TTupleItems[Key] extends object
35-
? Writeable<TTupleItems[Key], TVariant>
36-
: TTupleItems[Key];
37-
},
38-
]
39-
: [...TTupleItems]
40-
: TObject extends ReadonlyArray<infer TArrayItem>
41-
? TVariant extends "deep"
42-
? Array<TArrayItem extends object ? Writeable<TArrayItem, TVariant> : TArrayItem>
43-
: TArrayItem[]
44-
: TObject extends object
45-
? {
46-
-readonly [Key in keyof TObject]: TVariant extends "shallow"
47-
? TObject[Key]
48-
: TVariant extends "deep"
49-
? TObject[Key] extends object
50-
? Writeable<TObject[Key], TVariant>
51-
: TObject[Key]
52-
: never;
53-
}
54-
: TObject;
55-
56-
export type ExtractUnion<TObject, TVariant extends "keys" | "values" = "values"> = TObject extends
57-
| Array<infer TUnion>
58-
| ReadonlyArray<infer TUnion>
59-
| Set<infer TUnion>
60-
? TUnion
61-
: TObject extends Record<infer TKeys, infer TValues>
62-
? TVariant extends "keys"
63-
? TKeys
64-
: Prettify<Writeable<TValues, "deep">>
65-
: never;
66-
6719
export type NonEmptyArray<TArrayItem> = [TArrayItem, ...TArrayItem[]];
6820

6921
/* eslint-disable ts-eslint/no-explicit-any -- Any is needed so one can pass any prop type without type errors */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from "./common";
2+
export * from "./writeable";
3+
export * from "./union";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import type { Prettify } from "./common";
2+
import type { Writeable } from "./writeable";
3+
4+
type AllowOnlyFirst<TFirstType, TSecondType, TExclusionValue = never> = Prettify<
5+
// eslint-disable-next-line perfectionist/sort-intersection-types -- I want TFirstType to be first
6+
TFirstType & Partial<Record<keyof Omit<TSecondType, keyof TFirstType>, TExclusionValue>>
7+
>;
8+
9+
type AllowOnlyFirstRequired<TFirstType, TSecondType, TExclusionValue = never> = Prettify<
10+
// eslint-disable-next-line perfectionist/sort-intersection-types -- I want TFirstType to be first
11+
TFirstType & Record<keyof Omit<TSecondType, keyof TFirstType>, TExclusionValue>
12+
>;
13+
14+
type MergeTypes<
15+
TArrayOfTypes extends unknown[],
16+
TAccumulator = NonNullable<unknown>,
17+
> = TArrayOfTypes extends [infer TFirstType, ...infer TRest]
18+
? MergeTypes<TRest, TAccumulator & TFirstType>
19+
: TAccumulator;
20+
21+
/**
22+
* Type utility that extracts discriminated properties from a union of types.
23+
* Takes an array of types and returns a union of types where each type has unique properties.
24+
*
25+
* @template TArrayOfTypes Array of types to process
26+
* @template TExclusionValue Value to exclude from the resulting union
27+
* @template TAccumulator Accumulator for the resulting union
28+
* @template TMergedProperties Merged properties from all types
29+
*/
30+
31+
export type UnionDiscriminator<
32+
TArrayOfTypes extends unknown[],
33+
TExclusionValue = never,
34+
TAccumulator = never,
35+
TMergedProperties = MergeTypes<TArrayOfTypes>,
36+
> = TArrayOfTypes extends [infer TFirstType, ...infer TRest]
37+
? UnionDiscriminator<
38+
TRest,
39+
TExclusionValue,
40+
// eslint-disable-next-line perfectionist/sort-union-types -- Let TAccumulator be first
41+
TAccumulator | AllowOnlyFirst<TFirstType, TMergedProperties, TExclusionValue>,
42+
TMergedProperties
43+
>
44+
: TAccumulator;
45+
46+
/**
47+
* Type utility that extracts discriminated properties from a union of types.
48+
* Takes an array of types and returns a union of types where each type has unique properties.
49+
*
50+
* @template TArrayOfTypes - Array of types to process
51+
* @template TExclusionValue - Value to exclude from the resulting union
52+
* @template TAccumulator - Accumulator for the resulting union
53+
* @template TMergedProperties - Merged properties from all types
54+
*/
55+
export type UnionDiscriminatorRequired<
56+
TArrayOfTypes extends unknown[],
57+
TExclusionValue = never,
58+
TAccumulator = never,
59+
TMergedProperties = MergeTypes<TArrayOfTypes>,
60+
> = TArrayOfTypes extends [infer TFirstType, ...infer TRest]
61+
? UnionDiscriminatorRequired<
62+
TRest,
63+
TExclusionValue,
64+
// eslint-disable-next-line perfectionist/sort-union-types -- Let TAccumulator be first
65+
TAccumulator | AllowOnlyFirstRequired<TFirstType, TMergedProperties, TExclusionValue>,
66+
TMergedProperties
67+
>
68+
: TAccumulator;
69+
70+
export type ExtractUnion<TObject, TVariant extends "keys" | "values" = "values"> = TObject extends
71+
| Array<infer TUnion>
72+
| ReadonlyArray<infer TUnion>
73+
| Set<infer TUnion>
74+
? TUnion
75+
: TObject extends Record<infer TKeys, infer TValues>
76+
? TVariant extends "keys"
77+
? TKeys
78+
: Prettify<Writeable<TValues, "deep">>
79+
: never;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
export type WriteableVariantUnion = "deep" | "shallow";
2+
3+
/**
4+
* Makes all properties in an object type writeable (removes readonly modifiers).
5+
* Supports both shallow and deep modes, and handles special cases like arrays, tuples, and unions.
6+
* @template TObject - The object type to make writeable
7+
* @template TVariant - The level of writeable transformation ("shallow" | "deep")
8+
*/
9+
export type Writeable<
10+
TObject,
11+
TVariant extends WriteableVariantUnion = "shallow",
12+
> = TObject extends readonly [...infer TTupleItems]
13+
? TVariant extends "deep"
14+
? [
15+
...{
16+
[Key in keyof TTupleItems]: TTupleItems[Key] extends object
17+
? Writeable<TTupleItems[Key], TVariant>
18+
: TTupleItems[Key];
19+
},
20+
]
21+
: [...TTupleItems]
22+
: TObject extends ReadonlyArray<infer TArrayItem>
23+
? TVariant extends "deep"
24+
? Array<TArrayItem extends object ? Writeable<TArrayItem, TVariant> : TArrayItem>
25+
: TArrayItem[]
26+
: TObject extends object
27+
? {
28+
-readonly [Key in keyof TObject]: TVariant extends "shallow"
29+
? TObject[Key]
30+
: TVariant extends "deep"
31+
? TObject[Key] extends object
32+
? Writeable<TObject[Key], TVariant>
33+
: TObject[Key]
34+
: never;
35+
}
36+
: TObject;

packages/toolkit-type-helpers/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends": "@zayne-labs/tsconfig/bundler/dom/library",
2+
"extends": ["../../tsconfig.base.json", "@zayne-labs/tsconfig/bundler/dom/library"],
33

44
"compilerOptions": {
55
"baseUrl": ".",

packages/toolkit/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"dev": "pnpm build:dev --watch",
4545
"lint:attw": "attw --pack . --ignore-rules=cjs-resolves-to-esm",
4646
"lint:publint": "publint --strict .",
47-
"lint:type-check": "tsc --pretty --incremental -p tsconfig.json",
47+
"lint:type-check": "tsc --pretty -p tsconfig.json",
4848
"release": "pnpm publish --no-git-checks",
4949
"release:test": "pnpx pkg-pr-new publish"
5050
},

packages/toolkit/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"extends": "@zayne-labs/tsconfig/bundler/dom/library",
2+
"extends": ["../../tsconfig.base.json", "@zayne-labs/tsconfig/bundler/dom/library"],
33

44
"compilerOptions": {
55
"baseUrl": ".",

tsconfig.base.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"compilerOptions": {
3+
"composite": true
4+
}
5+
}

0 commit comments

Comments
 (0)