@manufac/sculptor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.github/workflows/feedback.yml +50 -0
  2. package/.github/workflows/inline.yml +119 -0
  3. package/.github/workflows/insights.yml +160 -0
  4. package/.husky/pre-commit +2 -0
  5. package/.prettierrc.mjs +2 -0
  6. package/Readme.md +1 -0
  7. package/dist/claude/feedback.yml +50 -0
  8. package/dist/claude/inline.yml +119 -0
  9. package/dist/claude/insights.yml +160 -0
  10. package/dist/guidelines/MantineCore.md +110 -0
  11. package/dist/guidelines/MantineForm.md +172 -0
  12. package/dist/guidelines/MantineHooks.md +230 -0
  13. package/dist/guidelines/React.md +502 -0
  14. package/dist/guidelines/Typescript.md +53 -0
  15. package/dist/guidelines/TypescriptNamingConventions.md +117 -0
  16. package/dist/guidelines/Zod.md +175 -0
  17. package/dist/tsc/index.js +11 -0
  18. package/dist/tsc/utils.js +116 -0
  19. package/dist/types/index.d.ts +3 -0
  20. package/dist/types/index.d.ts.map +1 -0
  21. package/dist/types/utils.d.ts +2 -0
  22. package/dist/types/utils.d.ts.map +1 -0
  23. package/eslint.config.mjs +8 -0
  24. package/package.json +43 -0
  25. package/src/claude/feedback.yml +50 -0
  26. package/src/claude/inline.yml +119 -0
  27. package/src/claude/insights.yml +160 -0
  28. package/src/guidelines/MantineCore.md +110 -0
  29. package/src/guidelines/MantineForm.md +172 -0
  30. package/src/guidelines/MantineHooks.md +230 -0
  31. package/src/guidelines/React.md +502 -0
  32. package/src/guidelines/Typescript.md +53 -0
  33. package/src/guidelines/TypescriptNamingConventions.md +117 -0
  34. package/src/guidelines/Zod.md +175 -0
  35. package/src/index.ts +14 -0
  36. package/src/utils.ts +142 -0
  37. package/tsconfig.json +113 -0
@@ -0,0 +1,502 @@
1
+ ### React Coding Guidelines
2
+
3
+ #### Component Structure
4
+
5
+ 1. **Component Naming**
6
+ - Each component name should be PascalCase.
7
+
8
+ ```ts
9
+ // Correct
10
+ function UserProfile(): JSX.Element {
11
+ return <div>Profile</div>;
12
+ }
13
+
14
+ // Incorrect
15
+ function userProfile(): JSX.Element {
16
+ return <div>Profile</div>;
17
+ }
18
+ ```
19
+
20
+ 2. **Component Declaration**
21
+ - Each component should be declared using the `function` keyword.
22
+
23
+ ```ts
24
+ // Correct
25
+ function Button(): JSX.Element {
26
+ return <button>Click</button>;
27
+ }
28
+
29
+ // Incorrect - arrow function
30
+ const Button = (): JSX.Element => {
31
+ return <button>Click</button>;
32
+ };
33
+ ```
34
+
35
+ 3. **Return Type**
36
+ - Each component should have an explicit return type of `JSX.Element`.
37
+
38
+ ```ts
39
+ function UserCard(): JSX.Element {
40
+ return <div>User Card</div>;
41
+ }
42
+ ```
43
+
44
+ 4. **Props Destructuring**
45
+ - Props should be destructured in the component parameters if less than 3.
46
+
47
+ Example:
48
+
49
+ ```ts
50
+ interface UserCardProps {
51
+ name: string;
52
+ email: string;
53
+ }
54
+
55
+ // Correct
56
+ function UserCard({ name, email }: UserCardProps): JSX.Element {
57
+ return (
58
+ <div>
59
+ <Text>{name}</Text>
60
+ <Text>{email}</Text>
61
+ </div>
62
+ );
63
+ }
64
+
65
+ // Incorrect
66
+ function UserCard(props: UserCardProps): JSX.Element {
67
+ return (
68
+ <div>
69
+ <Text>{props.name}</Text>
70
+ <Text>{props.email}</Text>
71
+ </div>
72
+ );
73
+ }
74
+ ```
75
+
76
+ 5. **Unused Props**
77
+ - Avoid passing props that are not used inside the component.
78
+
79
+ ```ts
80
+ interface ButtonProps {
81
+ label: string;
82
+ onClick: MouseEventHandler<HTMLButtonElement>;
83
+ }
84
+
85
+ // Correct - only pass what's needed
86
+ <Button label="Submit" onClick={handleClick} />
87
+
88
+ // Incorrect - passing unused prop
89
+ <Button label="Submit" onClick={handleClick} unused={someValue} />
90
+ ```
91
+
92
+ #### Type Safety
93
+
94
+ 6. **Avoid Wide Types**
95
+ - Avoid using wide types like `ReactNode` or `ReactElement` unless absolutely necessary.
96
+ - Only use wide types when the component genuinely accepts any valid React children.
97
+ - Always add a comment explaining why the wide type is necessary.
98
+
99
+ ```ts
100
+ interface CardProps {
101
+ // ReactNode is necessary here because this component accepts any valid React children
102
+ // including strings, numbers, elements, fragments, and portals
103
+ children: ReactNode;
104
+ title: string;
105
+ }
106
+
107
+ function Card({ children, title }: CardProps): JSX.Element {
108
+ return (
109
+ <div>
110
+ <Title>{title}</Title>
111
+ {children}
112
+ </div>
113
+ );
114
+ }
115
+ ```
116
+
117
+ 7. **Handler Type Safety**
118
+ - Each `onChange` handler, `onClick` handler, and other event handlers should have appropriately declared types.
119
+ - Use component-specific prop types or React's event handler types.
120
+
121
+ ```ts
122
+ import type { MouseEventHandler } from 'react';
123
+ import type { TextInputProps } from '@mantine/core';
124
+
125
+ interface FormProps {
126
+ onSubmit: MouseEventHandler<HTMLButtonElement>;
127
+ onChange: TextInputProps["onChange"];
128
+ }
129
+
130
+ function Form({ onSubmit, onChange }: FormProps): JSX.Element {
131
+ return (
132
+ <form>
133
+ <TextInput onChange={onChange} />
134
+ <Button onClick={onSubmit}>Submit</Button>
135
+ </form>
136
+ );
137
+ }
138
+ ```
139
+
140
+ #### Code Organization
141
+
142
+ 8. **Handler Functions**
143
+ - All handlers declared inside the component should be arrow functions.
144
+
145
+ ```ts
146
+ function UserForm(): JSX.Element {
147
+ // Correct - arrow function
148
+ const handleSubmit: MouseEventHandler<HTMLButtonElement> = (event) => {
149
+ event.preventDefault();
150
+ // Handle submit
151
+ };
152
+
153
+ // Incorrect - function declaration
154
+ function handleSubmit(event: MouseEvent<HTMLButtonElement>): void {
155
+ event.preventDefault();
156
+ }
157
+
158
+ return <Button onClick={handleSubmit}>Submit</Button>;
159
+ }
160
+ ```
161
+
162
+ 9. **Utility Functions**
163
+ - Extract business logic, calculations, and data transformations into utility functions.
164
+ - Move utility functions outside the component into a `utils.ts` file.
165
+
166
+ ```ts
167
+ // utils.ts
168
+ export function calculateTotal(items: CartItem[]): number {
169
+ return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
170
+ }
171
+
172
+ export function formatCurrency(amount: number): string {
173
+ return new Intl.NumberFormat('en-US', {
174
+ style: 'currency',
175
+ currency: 'USD',
176
+ }).format(amount);
177
+ }
178
+
179
+ // CartSummary/index.tsx
180
+ import { calculateTotal, formatCurrency } from './utils';
181
+
182
+ function CartSummary({ items }: CartSummaryProps): JSX.Element {
183
+ const total = calculateTotal(items);
184
+
185
+ return <Text>{formatCurrency(total)}</Text>;
186
+ }
187
+ ```
188
+
189
+ 10. **File Naming Convention**
190
+ - All component file names should follow the pattern `ComponentName/index.tsx`.
191
+ - If a file does not contain any component, its extension should not be `.tsx`.
192
+
193
+ ```text
194
+ src/
195
+ components/
196
+ UserProfile/
197
+ index.tsx // Component file
198
+ utils.ts // Utility functions (no JSX)
199
+ types.ts // Type definitions (no JSX)
200
+ styles.module.css
201
+ Button/
202
+ index.tsx
203
+ ```
204
+
205
+ #### Data Fetching & Mutations
206
+
207
+ 11. **TanStack Query**
208
+ - Use TanStack Query for all data fetching and mutations.
209
+ - Use `useQuery` for fetching data.
210
+ - Use `useMutation` for creating, updating, or deleting data.
211
+ - Define query keys in a separate constants file for reusability.
212
+
213
+ ```ts
214
+ // queryKeys.ts
215
+ export const queryKeys = {
216
+ users: {
217
+ all: ['users'] as const,
218
+ detail: (id: string) => ['users', id] as const,
219
+ },
220
+ };
221
+
222
+ // useGetUser.ts
223
+ import { useQuery } from '@tanstack/react-query';
224
+ import { queryKeys } from './queryKeys';
225
+
226
+ interface User {
227
+ id: string;
228
+ name: string;
229
+ email: string;
230
+ }
231
+
232
+ function fetchUser(userId: string): Promise<User> {
233
+ return fetch(`/api/users/${userId}`).then((res) => res.json());
234
+ }
235
+
236
+ export function useGetUser(userId: string) {
237
+ return useQuery({
238
+ queryKey: queryKeys.users.detail(userId),
239
+ queryFn: () => fetchUser(userId),
240
+ });
241
+ }
242
+
243
+ // useUpdateUser.ts
244
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
245
+ import { queryKeys } from './queryKeys';
246
+
247
+ interface UpdateUserParams {
248
+ userId: string;
249
+ data: Partial<User>;
250
+ }
251
+
252
+ function updateUser(params: UpdateUserParams): Promise<User> {
253
+ return fetch(`/api/users/${params.userId}`, {
254
+ method: 'PATCH',
255
+ body: JSON.stringify(params.data),
256
+ }).then((res) => res.json());
257
+ }
258
+
259
+ export function useUpdateUser() {
260
+ const queryClient = useQueryClient();
261
+
262
+ return useMutation({
263
+ mutationFn: updateUser,
264
+ onSuccess: (data) => {
265
+ queryClient.invalidateQueries({
266
+ queryKey: queryKeys.users.detail(data.id),
267
+ });
268
+ },
269
+ });
270
+ }
271
+
272
+ // UserProfile/index.tsx
273
+ function UserProfile({ userId }: UserProfileProps): JSX.Element {
274
+ const { data: user, isPending } = useGetUser(userId);
275
+ const { mutate: updateUser } = useUpdateUser();
276
+
277
+ const handleUpdate: MouseEventHandler<HTMLButtonElement> = () => {
278
+ updateUser({ userId, data: { name: 'New Name' } });
279
+ };
280
+
281
+ if (isPending) {
282
+ return <Loader />;
283
+ }
284
+
285
+ return (
286
+ <div>
287
+ <Text>{user?.name}</Text>
288
+ <Button onClick={handleUpdate}>Update</Button>
289
+ </div>
290
+ );
291
+ }
292
+ ```
293
+
294
+ #### Modern React Patterns
295
+
296
+ 12. **React 19 Best Practices**
297
+ - Follow React 19 best practices from https://react.dev/blog/2024/12/05/react-19
298
+ - Use React 19 Actions for form submissions and mutations when not using TanStack Query.
299
+ - Prefer the `use` hook for reading promises and context.
300
+ - Utilize ref callbacks for DOM measurements.
301
+ - Use `useOptimistic` for optimistic UI updates.
302
+
303
+ ```ts
304
+ // Using Actions for form submission (when not using TanStack Query)
305
+ import { useActionState } from 'react';
306
+
307
+ async function submitForm(prevState: FormState, formData: FormData): Promise<FormState> {
308
+ const name = formData.get('name') as string;
309
+ // Process form data
310
+ return { success: true, message: 'Form submitted' };
311
+ }
312
+
313
+ function ContactForm(): JSX.Element {
314
+ const [state, formAction, isPending] = useActionState(submitForm, {
315
+ success: false,
316
+ message: '',
317
+ });
318
+
319
+ return (
320
+ <form action={formAction}>
321
+ <TextInput name="name" />
322
+ <Button type="submit" loading={isPending}>
323
+ Submit
324
+ </Button>
325
+ {state.message !== '' ? <Text>{state.message}</Text> : null}
326
+ </form>
327
+ );
328
+ }
329
+ ```
330
+
331
+ 13. **Escape hatches**
332
+ - Refer https://react.dev/learn/escape-hatches for detecting escape hatches.
333
+ - Any usage of an escape hatch must include a comment explaining:
334
+ - Why it is necessary
335
+ - Why a declarative alternative was not possible
336
+ - What risk it introduces
337
+
338
+ #### Performance Optimization
339
+
340
+ 14. **Memoization**
341
+ - Use `memo` to prevent unnecessary re-renders of components that receive the same props.
342
+ - Use `useMemo` for expensive calculations.
343
+ - Use `useCallback` for functions passed as props to memoized components.
344
+ - Always add comments explaining why memoization is necessary.
345
+
346
+ ```ts
347
+ import { memo, useMemo, useCallback } from 'react';
348
+
349
+ interface ExpensiveListProps {
350
+ items: Item[];
351
+ onItemClick: (id: string) => void;
352
+ }
353
+
354
+ // Memoized to prevent re-renders when parent re-renders
355
+ // but items and onItemClick remain the same
356
+ export const ExpensiveList = memo(function ExpensiveList({
357
+ items,
358
+ onItemClick,
359
+ }: ExpensiveListProps): JSX.Element {
360
+ // Expensive calculation memoized to avoid recalculation on every render
361
+ const sortedItems = useMemo(() => {
362
+ return [...items].sort((a, b) => a.priority - b.priority);
363
+ }, [items]);
364
+
365
+ return (
366
+ <Stack>
367
+ {sortedItems.map((item) => (
368
+ <ItemCard key={item.id} item={item} onClick={onItemClick} />
369
+ ))}
370
+ </Stack>
371
+ );
372
+ });
373
+
374
+ function ItemList(): JSX.Element {
375
+ const { data: items } = useGetItems();
376
+
377
+ // Memoized because it's passed to a memoized component
378
+ const handleItemClick = useCallback((id: string) => {
379
+ console.log('Item clicked:', id);
380
+ }, []);
381
+
382
+ return <ExpensiveList items={items ?? []} onItemClick={handleItemClick} />;
383
+ }
384
+ ```
385
+
386
+ 15. **List Rendering**
387
+ - Always provide a unique, stable `key` prop when rendering lists.
388
+ - Use unique identifiers (IDs) as keys, not array indices.
389
+ - Add a comment if array index must be used as a key.
390
+
391
+ ```ts
392
+ // Correct - using unique ID
393
+ function UserList({ users }: UserListProps): JSX.Element {
394
+ return (
395
+ <Stack>
396
+ {users.map((user) => (
397
+ <UserCard key={user.id} user={user} />
398
+ ))}
399
+ </Stack>
400
+ );
401
+ }
402
+
403
+ // Incorrect - using array index
404
+ function UserList({ users }: UserListProps): JSX.Element {
405
+ return (
406
+ <Stack>
407
+ {users.map((user, index) => (
408
+ <UserCard key={index} user={user} />
409
+ ))}
410
+ </Stack>
411
+ );
412
+ }
413
+
414
+ // Acceptable with comment - when items have no unique identifier
415
+ function StaticList({ items }: StaticListProps): JSX.Element {
416
+ return (
417
+ <Stack>
418
+ {items.map((item, index) => (
419
+ // Index used as key because items are static and have no unique identifier
420
+ <Text key={index}>{item}</Text>
421
+ ))}
422
+ </Stack>
423
+ );
424
+ }
425
+ ```
426
+
427
+ #### Conditional Rendering
428
+
429
+ 16. **Conditional Rendering Patterns**
430
+ - Use explicit `if` statements for complex conditions or when performing side effects.
431
+ - Use ternary operators for simple conditional rendering.
432
+ - Use logical `&&` operator only for boolean conditions, never for truthy/falsy checks.
433
+ - Always ensure the left side of `&&` evaluates to a boolean.
434
+
435
+ ```ts
436
+ function UserProfile({ user }: UserProfileProps): JSX.Element {
437
+ // Correct - explicit boolean check with &&
438
+ return (
439
+ <Stack>
440
+ {user.isActive === true && <Badge>Active</Badge>}
441
+
442
+ {/* Correct - ternary for simple either/or */}
443
+ {user.isPremium === true ? <PremiumBadge /> : <FreeBadge />}
444
+ </Stack>
445
+ );
446
+ }
447
+
448
+ function ProductList({ products }: ProductListProps): JSX.Element {
449
+ // Incorrect - truthy check with && can render 0
450
+ // {products.length && <Text>Products available</Text>}
451
+
452
+ // Correct - explicit boolean check
453
+ if (products.length === 0) {
454
+ return <Text>No products available</Text>;
455
+ }
456
+
457
+ return (
458
+ <Stack>
459
+ {products.map((product) => (
460
+ <ProductCard key={product.id} product={product} />
461
+ ))}
462
+ </Stack>
463
+ );
464
+ }
465
+ ```
466
+
467
+ #### Custom Hooks
468
+
469
+ 17. **Custom Hook Conventions**
470
+ - Custom hooks must start with `use` prefix.
471
+ - Extract reusable logic into custom hooks.
472
+ - Custom hooks should have explicit return types.
473
+ - Place custom hooks in a `hooks` directory.
474
+
475
+ ```ts
476
+ // hooks/useLocalStorage.ts
477
+ import { useState, useEffect } from "react";
478
+
479
+ interface UseLocalStorageReturn<T> {
480
+ value: T;
481
+ setValue: (value: T) => void;
482
+ removeValue: () => void;
483
+ }
484
+
485
+ export function useLocalStorage<T>(key: string, initialValue: T): UseLocalStorageReturn<T> {
486
+ const [value, setValue] = useState<T>(() => {
487
+ const item = window.localStorage.getItem(key);
488
+ return item !== null ? JSON.parse(item) : initialValue;
489
+ });
490
+
491
+ useEffect(() => {
492
+ window.localStorage.setItem(key, JSON.stringify(value));
493
+ }, [key, value]);
494
+
495
+ const removeValue = (): void => {
496
+ window.localStorage.removeItem(key);
497
+ setValue(initialValue);
498
+ };
499
+
500
+ return { value, setValue, removeValue };
501
+ }
502
+ ```
@@ -0,0 +1,53 @@
1
+ ### Coding conventions guidelines
2
+
3
+ 1. All top-level and named functions must be declared using a `function` declaration.
4
+ 2. Arrow functions are permitted only for callbacks and inline anonymous functions.
5
+ 3. Function expression is not allowed.
6
+ 4. All functions must explicitly declare their return type.
7
+ 5. If a function has more than 2 parameters, require a single object parameter typed using an explicit interface or type alias.
8
+ 6. Do not use short-circuiting operators (`&&`, `||`) **as a substitute for control-flow or side-effect execution** (e.g. conditionally calling functions or executing statements).
9
+
10
+ - Short-circuiting operators **are allowed** in pure boolean expressions, conditions, return values, and assignments.
11
+ - Use explicit `if` statements or a ternary operator when performing branching logic or side effects.
12
+
13
+ 5. Condition checks must evaluate to boolean values:
14
+ - For boolean variables, use direct checks (e.g. `if (isValid)`).
15
+ - For non-boolean expressions, use explicit comparisons (e.g. `array.length === 0`).
16
+ 6. Use `array.at(0)` to access the element of an array.
17
+ 7. For derived objects, use `as const satisfies`:
18
+
19
+ ```ts
20
+ interface XYZ {
21
+ name: string;
22
+ email: string;
23
+ }
24
+
25
+ const XYZFormNames = {
26
+ name: "name",
27
+ email: "email",
28
+ } as const satisfies Record<keyof XYZ, keyof XYZ>;
29
+
30
+ const XYZFormLabels = {
31
+ name: "Full Name",
32
+ email: "Email Address",
33
+ } as const satisfies Record<keyof XYZ, string>;
34
+ ```
35
+
36
+ 8. For un-derived objects use type declaration.
37
+ Example:
38
+
39
+ ```ts
40
+ const User = {
41
+ name: "abc",
42
+ email: "xyz",
43
+ }; // This is incorrect
44
+
45
+ interface User {
46
+ name: string;
47
+ email: string;
48
+ }
49
+ const UserDetails: User = {
50
+ name: "abc",
51
+ email: "xyz",
52
+ }; // This is correct.
53
+ ```
@@ -0,0 +1,117 @@
1
+ ### Variable naming guidelines
2
+
3
+ - Identify project domain using project codebase.
4
+ - Use domain-driven development guidelines regarding naming conventions.
5
+ - Abbreviated/acronym in the variable names should be all in uppercase.
6
+ example:
7
+ - testURL is preferred instead of testUrl
8
+ - chartID is preferred instead of chartId
9
+ - userIDs is preferred instead of userIds
10
+ - httpURL is preferred instead of httpUrl
11
+ - Check for spelling errors in the variable names.
12
+ - If a compound word exists as a single dictionary word, it should not have camel casing. When uncertain if a compound word is a single dictionary entry, consult standard dictionaries (Merriam-Webster, Oxford)
13
+ example:
14
+ - subtitle is correct while subTitle is incorrect
15
+ - database is correct while dataBase is incorrect
16
+ - username is correct while userName is incorrect
17
+ - When a compound dictionary word contains an acronym, the acronym rule takes precedence.
18
+ example:
19
+ - databaseURL is correct while databaseUrl is incorrect
20
+ - Global variables should be PascalCase except for global variables that are arrow functions or higher order functions, which should be camelCase.
21
+ example:
22
+ - `const UserConfig = {...}` (PascalCase for regular global variable)
23
+ - `const fetchData = () => {...}` (camelCase for global arrow function)
24
+ - `const withAuth = (component) => {...}` (camelCase for global HOF)
25
+ - Variable names should be in camelCase or PascalCase. No other format is accepted.
26
+ - Use camelCase for local variables and function parameters
27
+ - Use PascalCase for classes, components, and global variables but functions names should not use pascal case.
28
+ - Snake cases or screaming snake case are not accepted. We are intentionally avoiding the snake case naming for constants even though it is preferred by JS conventions.
29
+ example:
30
+ - `const MAX_RETRY_COUNT = 5` is incorrect while `const MaxRetryCount = 5` is correct
31
+ - `const user_id = response.user_id` is incorrect while `const userID = response.user_id` is correct (follows camelCase with uppercase acronym rule)
32
+ - When mapping API responses with nested objects, transform all levels from snake_case to camelCase.
33
+ example:
34
+ - `const user = { userID: response.user_id, profileData: response.profile_data }` is correct
35
+ - Kebab case values are allowed only in object values.
36
+ example:
37
+ - `const styles = { className: "user-profile" }` (kebab-case string value is OK)
38
+ - `const config = { "user-id": 123 }` (kebab-case as object key is incorrect)
39
+ - Use only named imports and exports.
40
+ - All boolean variables should start with a helping verb: `is`, `has`, `can`, or `should`.
41
+ example:
42
+ - isNull, isValid, isLoading
43
+ - hasPermission, hasError
44
+ - canEdit, canDelete
45
+ - shouldUpdate, shouldRender
46
+ - Negative boolean conditions should be avoided unless necessary for code readability. When used, they must be accompanied by a comment explaining the positive context.
47
+ example:
48
+ - ❌ `const isInvalid = checkValidation()` (avoid negative naming)
49
+ - ✅ `const isValid = checkValidation()` (use positive naming with ! operator when needed)
50
+ - ✅ `const isDisabled = true // Indicates the button cannot be clicked` (acceptable with explanatory comment)
51
+
52
+ ### Types naming conventions
53
+
54
+ - Interface names should be PascalCase and they should not start with `I`.
55
+ example:
56
+ - IUser is invalid while User is valid interface name
57
+
58
+ ```ts
59
+ // ❌ Invalid
60
+ interface IUser {
61
+ name: string;
62
+ }
63
+
64
+ // ✅ Valid
65
+ interface User {
66
+ name: string;
67
+ }
68
+ ```
69
+
70
+ - Type alias names should be PascalCase.
71
+ example:
72
+ - `type UserRole = string | number` (PascalCase type name)
73
+ - Avoid type aliases unless necessary for union types or mapped types. Prefer interfaces for object shapes.
74
+ example:
75
+ - `type UserRole = string | number` (acceptable for union types)
76
+ - `type User = { name: string }` (avoid, use interface instead)
77
+ - `interface User { name: string }` (preferred)
78
+ - Union types are allowed when used with:
79
+ - Primitive types
80
+ - `type UserRole = string | number` (PascalCase type name)
81
+ - Complex types (interfaces, objects)
82
+ ```ts
83
+ interface User {
84
+ email: string;
85
+ role: string;
86
+ }
87
+ type Actor = string | User;
88
+ ```
89
+ - Enums should not be used. Use union types with const objects instead.
90
+ example:
91
+ - ❌ `enum UserRole { Admin, Editor, Viewer }`
92
+ - Predefined / Finite Domain Values
93
+ - If a value is predefined and limited (e.g. roles like admin, user, editor):
94
+ - You must create a const dictionary
95
+ - The dictionary name must be PascalCase
96
+ - Use as const
97
+ - Derive the type using keyof typeof
98
+
99
+ ```TS
100
+ const UserRoles = {
101
+ Admin: 'admin',
102
+ Editor: 'editor',
103
+ Viewer: 'viewer',
104
+ } as const
105
+
106
+ type UserRole = (typeof UserRoles)[keyof typeof UserRoles]
107
+
108
+ interface User {
109
+ email: string
110
+ role: UserRole
111
+ }
112
+ ```
113
+
114
+ ### File naming conventions
115
+
116
+ 1. All `*.ts` file name should be kebab cased.
117
+ 2. All `*.md` file name should be Pascal case.