|
1 | | -## Language: TypeScript |
2 | 1 | --- |
3 | | -description: 'Brief description of the instruction purpose and scope' |
4 | | -applyTo: '*.ts' |
| 2 | +description: 'Guidelines for writing high-quality, maintainable TypeScript code with best practices for logging, error handling, code organization, naming, formatting, and style.' |
| 3 | +applyTo: '**/*.ts, **/*.tsx' |
5 | 4 | --- |
6 | | -### Project Style (clinicalView) |
7 | | -- Use named exports (no default) |
8 | | -- Prefer small pure functions; pass `logger: Logger` explicitly rather than using globals. |
9 | 5 |
|
10 | | -### JSON Schema Pattern |
11 | | -- Define schemas with `as const satisfies JSONSchema` |
12 | | -- Export corresponding types using `FromSchema<typeof schema>` right after the schema object. |
13 | | -- Reuse existing element schemas instead of inlining duplicates. |
| 6 | +# TypeScript Development Guidelines |
14 | 7 |
|
15 | | -### Coding / Mapping Conventions |
16 | | -- Reuse mapping constants for code/display pairs (e.g. prescription type, performer site, status maps); do not hardcode strings already present. |
17 | | -- For UUIDs use `randomUUID()` from `crypto` |
18 | | -- When adding new status or code systems, centralize enums in a schema file under `src/schema/` and update maps in `fhirMaps.ts`. |
| 8 | +This document provides instructions for generating, reviewing, and maintaining TypeScript code. It is designed to guide Copilot and developers in producing domain-specific, robust, and maintainable code across a variety of TypeScript projects. |
| 9 | + |
| 10 | +## General Instructions |
| 11 | + |
| 12 | +- Use modern TypeScript features and syntax. |
| 13 | +- Prefer explicit types and interfaces for clarity and safety. |
| 14 | +- Organize code into logical modules and folders. |
| 15 | +- Write code that is easy to read, test, and maintain. |
| 16 | + |
| 17 | +## Best Practices |
| 18 | + |
| 19 | +- Use `const` and `let` appropriately; avoid `var`. |
| 20 | +- Prefer arrow functions for callbacks and concise function expressions. |
| 21 | +- Use destructuring for objects and arrays to improve readability. |
| 22 | +- Avoid magic numbers and hardcoded values; use named constants. |
| 23 | +- Keep functions pure and side-effect free when possible. |
| 24 | + |
| 25 | +## Code Standards |
| 26 | + |
| 27 | +### Naming Conventions |
| 28 | + |
| 29 | +- Use `camelCase` for variables, functions, and object properties. |
| 30 | +- Use `PascalCase` for types, interfaces, classes, and enums. |
| 31 | +- Use descriptive names; avoid abbreviations except for well-known acronyms. |
| 32 | +- Prefix boolean variables with `is`, `has`, or `should` (e.g., `isActive`). |
| 33 | + |
| 34 | +### File Organization |
| 35 | + |
| 36 | +- Group related code in folders (e.g., `src/`, `tests/`, `lib/`). |
| 37 | +- Place one class, interface, or component per file when possible. |
| 38 | +- Name files using `kebab-case` (e.g., `user-service.ts`). |
| 39 | +- Keep test files close to the code they test (e.g., `src/foo.ts` and `tests/foo.test.ts`). |
| 40 | + |
| 41 | +### Formatting and Style |
| 42 | + |
| 43 | +- Use 2 spaces for indentation. |
| 44 | +- Limit lines to 120 characters. |
| 45 | +- Use single quotes for strings. |
| 46 | +- Always use semicolons. |
| 47 | +- Prefer trailing commas in multiline objects and arrays. |
| 48 | +- Use ESLint and Prettier for consistent formatting. |
| 49 | + |
| 50 | +## Architecture/Structure |
| 51 | + |
| 52 | +- Separate business logic from API handlers and utility functions. |
| 53 | +- Use interfaces and types to define data structures and function signatures. |
| 54 | +- Organize code by feature or domain when scaling projects. |
| 55 | +- Use dependency injection for testability and flexibility. |
| 56 | + |
| 57 | +## Common Patterns |
| 58 | + |
| 59 | +### Logging |
| 60 | + |
| 61 | +- Use a centralized logging utility or library. |
| 62 | +- Log errors, warnings, and important events with context. |
| 63 | +- Avoid logging sensitive information. |
| 64 | +- Example: |
| 65 | + |
| 66 | + ```typescript |
| 67 | + import { logger } from './utils/logger'; |
| 68 | + |
| 69 | + logger.info('Fetching user data', { userId }); |
| 70 | + logger.error('Failed to fetch user', { error }); |
| 71 | + ``` |
19 | 72 |
|
20 | 73 | ### Error Handling |
21 | | -- Represent conditional outcomes using discriminated unions similar to `ParsedSpineResponse` in [`parseSpineResponse`](packages/clinicalView/src/parseSpineResponse.ts). |
22 | | -- Return `[result, errors]` tuples for validators (pattern in `validateRequest` declaration). |
23 | | - |
24 | | -### Logging & Middleware |
25 | | -- Instantiate `Logger` from `@aws-lambda-powertools/logger`; pass through layers/functions (see [`handler`](packages/clinicalView/src/handler.ts)). |
26 | | -- For new handlers wrap with `middy` and apply existing middlewares: `injectLambdaContext`, `httpHeaderNormalizer`, `inputOutputLogger`, shared error handler. |
27 | | - |
28 | | -### FHIR Resource Construction |
29 | | -- Use existing helper logic patterns in [`generateFhirResponse`](packages/clinicalView/src/generateFhirResponse.ts): derive `intent` from `INTENT_MAP`, map therapy/course codes from constants, conditionally include optional fields with object spread (`...(condition ? {field} : {})`). |
30 | | -- Maintain consistent array wrapping for singular FHIR arrays (e.g. `coding: [{ ... }]`, `identifier: [{ ... }]`, `dosageInstruction: [{ text }]`). |
31 | | - |
32 | | -### Extensions |
33 | | -- Follow existing structure: `{ url, valueCoding }` or `{ url, extension: [...] }` (see [`extensions`](packages/clinicalView/src/schema/extensions.ts)). |
34 | | -- Do not invent new URL namespaces; reuse `https://fhir.nhs.uk/...` patterns. |
35 | | - |
36 | | -### Testing |
37 | | -- Snapshot-like expectations in tests should mirror object shape used in generation; keep field order stable to reduce churn (see tests in `tests/testGenerateFhirResponse.test.ts`). |
38 | | - |
39 | | -### General TS Practices |
40 | | -- Use `as const` for literal enums & maps to preserve string literal types. |
41 | | -- Avoid `any`; prefer explicit interfaces or inferred types from schemas. |
42 | | -- Prefer `Record<Key, Value>` for code/display maps (see `PRESCRIPTION_TYPE_MAP` etc.). |
43 | | -- Use union narrowing via presence checks instead of optional chaining inside tight loops for performance-critical parsing. |
44 | | - |
45 | | -### When Adding Code |
46 | | -1. Add schema first (if new structure). |
47 | | -2. Export type via `FromSchema`. |
48 | | -3. Extend maps in `fhirMaps.ts` if introducing coded values. |
49 | | -4. Update generator logic keeping ordering conventions. |
50 | | -5. Add/adjust tests in `packages/clinicalView/tests/`. |
51 | | -6. Ensure new exports are re-exported in index only if needed by other packages. |
52 | | - |
53 | | -### Avoid |
54 | | -- Duplicating code system enums already defined. |
55 | | -- Introducing default exports. |
56 | | -- Hardcoding display text strings when a map exists. |
57 | | -- Using mutable push patterns where direct literal construction is clearer. |
58 | | - |
59 | | -### Example Pattern (New Simple Schema) |
60 | | -```ts |
61 | | -import {FromSchema, JSONSchema} from "json-schema-to-ts" |
62 | | - |
63 | | -export const exampleResource = { |
64 | | - type: "object", |
65 | | - properties: { |
66 | | - resourceType: {type: "string", enum: ["Example"]}, |
67 | | - id: {type: "string"}, |
68 | | - status: {type: "string", enum: ["active", "inactive"]} |
69 | | - }, |
70 | | - required: ["resourceType", "id", "status"] |
71 | | -} as const satisfies JSONSchema |
72 | | - |
73 | | -export type ExampleResourceType = FromSchema<typeof exampleResource> |
74 | | -``` |
75 | | -
|
76 | | -Keep additions consistent with existing clinicalView module structure. |
| 74 | + |
| 75 | +- Use `try/catch` for asynchronous code and error-prone operations. |
| 76 | +- Throw custom error types for domain-specific errors. |
| 77 | +- Always handle errors gracefully and provide meaningful messages. |
| 78 | +- Example: |
| 79 | + |
| 80 | + ```typescript |
| 81 | + try { |
| 82 | + const result = await fetchData(); |
| 83 | + } catch (error) { |
| 84 | + logger.error('Data fetch failed', { error }); |
| 85 | + throw new DataFetchError('Unable to fetch data'); |
| 86 | + } |
| 87 | + ``` |
| 88 | + |
| 89 | +### Type Safety |
| 90 | + |
| 91 | +- Prefer interfaces and types over `any`. |
| 92 | +- Use type guards and assertions when necessary. |
| 93 | +- Example: |
| 94 | + |
| 95 | + ```typescript |
| 96 | + interface User { |
| 97 | + id: string; |
| 98 | + name: string; |
| 99 | + } |
| 100 | +
|
| 101 | + function isUser(obj: any): obj is User { |
| 102 | + return typeof obj.id === 'string' && typeof obj.name === 'string'; |
| 103 | + } |
| 104 | + ``` |
| 105 | + |
| 106 | +## Security |
| 107 | + |
| 108 | +- Validate and sanitize all external input. |
| 109 | +- Avoid exposing sensitive data in logs or error messages. |
| 110 | +- Use environment variables for secrets and configuration. |
| 111 | +- Keep dependencies up to date and audit regularly. |
| 112 | + |
| 113 | +## Performance |
| 114 | + |
| 115 | +- Minimize synchronous blocking operations. |
| 116 | +- Use async/await for asynchronous code. |
| 117 | +- Avoid unnecessary computations inside render or handler functions. |
| 118 | + |
| 119 | +## Testing |
| 120 | + |
| 121 | +- Write unit tests for all business logic. |
| 122 | +- Use Jest or similar frameworks for testing. |
| 123 | +- Mock external dependencies in tests. |
| 124 | +- Example test file structure: |
| 125 | + |
| 126 | + ``` |
| 127 | + src/ |
| 128 | + handler.ts |
| 129 | + tests/ |
| 130 | + handler.test.ts |
| 131 | + ``` |
| 132 | + |
| 133 | +## Examples and Code Snippets |
| 134 | + |
| 135 | +### Good Example |
| 136 | + |
| 137 | + ```typescript |
| 138 | + interface Prescription { |
| 139 | + id: string; |
| 140 | + medication: string; |
| 141 | + issuedDate: Date; |
| 142 | + } |
| 143 | +
|
| 144 | + function getPrescription(id: string): Prescription | null { |
| 145 | + // Implementation |
| 146 | + } |
| 147 | + ``` |
| 148 | + |
| 149 | +### Bad Example |
| 150 | + |
| 151 | + ```typescript |
| 152 | + function getPrescription(id) { |
| 153 | + // No type safety, unclear return type |
| 154 | + } |
| 155 | + ``` |
| 156 | + |
| 157 | +## Validation and Verification |
| 158 | + |
| 159 | +- Build: `npm run build` |
| 160 | +- Lint: `npm run lint` |
| 161 | +- Format: `npm run format` |
| 162 | +- Test: `npm test` |
| 163 | + |
| 164 | +## Maintenance |
| 165 | + |
| 166 | +- Review and update instructions as dependencies or frameworks change. |
| 167 | +- Update examples to reflect current best practices. |
| 168 | +- Remove deprecated patterns and add new ones as needed. |
| 169 | +- Ensure glob patterns match the intended files. |
| 170 | + |
| 171 | +## Additional Resources |
| 172 | + |
| 173 | +- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/) |
| 174 | +- [ESLint TypeScript Plugin](https://typescript-eslint.io/) |
| 175 | +- [Prettier Documentation](https://prettier.io/docs/en/options.html) |
0 commit comments