Skip to content

refactor: eliminate unsafe type assertions in widget components#612

Merged
pethers merged 3 commits into
mainfrom
copilot/eliminate-unsafe-type-assertions
Nov 15, 2025
Merged

refactor: eliminate unsafe type assertions in widget components#612
pethers merged 3 commits into
mainfrom
copilot/eliminate-unsafe-type-assertions

Conversation

Copilot AI commented Nov 15, 2025

Copy link
Copy Markdown
Contributor

Pull Request Description

Eliminated 26 unsafe type assertions (as any) across widget components that violated TypeScript strict mode requirements. The codebase relied on runtime type coercion to access optional service methods and properties, creating potential runtime errors and breaking IntelliSense.

Changes

Type Guard Infrastructure

  • Added hasMethod<T, K>() utility for runtime method existence checking with proper type narrowing
  • Updated hasMethod return type from Function to (...args: any[]) => any for stricter TypeScript compliance
  • Replaces typeof (obj as any).method === "function" pattern throughout codebase

Widget Refactoring

  • TechnicalDetailsWidget (10 fixes): Replaced unsafe property access ((details as any)?.technologies) with optimized typed helper function using Object.prototype.hasOwnProperty.call() and minimal type assertions
  • ValueCreationWidget (8 fixes): Converted service method checks to type-safe guards; removed non-existent method calls
  • CostEstimationWidget (2 fixes): Enhanced validation with content checking (expertise.every(item => isString(item))) before type casting for improved type safety
  • SecuritySummaryWidget (9 fixes): Removed [key: string]: any index signatures; corrected method signatures (getImplementationTimegetTotalImplementationTime)

Example

Before:

if (typeof (ciaContentService as any).getBusinessValueMetrics === "function") {
  const metrics = (ciaContentService as any).getBusinessValueMetrics(levels);
}

After:

if (hasMethod(ciaContentService, 'getBusinessValueMetrics')) {
  const metrics = ciaContentService.getBusinessValueMetrics(levels);
}

Code Review Improvements

Based on code review feedback, the following enhancements were made to further improve type safety:

  1. hasMethod Type Signature: Changed return type from Function to (...args: any[]) => any for better TypeScript strict mode compliance
  2. getOptionalProperty Optimization: Reduced type assertions and improved null checking using Object.prototype.hasOwnProperty.call() with single minimal cast and value validation
  3. Expertise Validation Enhancement: Added runtime content validation with expertise.every(item => isString(item)) before type casting to ensure array elements are actually strings

Type of Change

  • 🔄 Code Quality & Refactoring
  • 🔒 Security & Compliance

Component(s) Modified

  • Security Summary Widget
  • Cost Estimation Widget
  • Value Creation Widget
  • Constants / Data Model

CIA Impact Area

  • Confidentiality
  • Integrity
  • Availability

Security Level Impact

  • Basic
  • Moderate
  • High
  • Very High

Test Coverage Impact

  • N/A - Only modifying high-coverage areas

Testing Performed

  • Unit tests added/updated
  • Manual testing completed
  • Security validation performed

All 1,295 existing tests pass. TypeScript strict mode compilation successful.

Screenshots/Examples

N/A - Type safety refactoring with no UI changes

Related Issues

Closes #73

Checklist

  • My PR title follows the conventional commit format (e.g., feat: add new feature)
  • Code follows project coding standards
  • Tests are passing
  • Documentation has been updated (if applicable)
  • Security compliance is maintained or improved
  • Changes have been reviewed for performance impact
  • Breaking changes are documented (if any)

Additional Notes

Zero remaining as any assertions in widget layer. All service method calls now enforce correct signatures at compile time. Enhanced type guards with stricter function signatures and improved runtime validation reduce type assertion chains while maintaining full backward compatibility.

Original prompt

This section details on the original issue you should resolve

<issue_title>🔒 Eliminate 20+ unsafe type assertions (as any) in widget components</issue_title>
<issue_description>## 🎯 Objective
Eliminate 20+ unsafe type assertions (as any) in widget components to improve type safety, prevent runtime errors, and align with TypeScript strict mode policy.

📋 Background

TypeScript React agent guidelines mandate explicit types and prohibit any usage. Current analysis reveals 20+ type safety violations in widget components:

Files with Type Safety Issues:

  • TechnicalDetailsWidget.tsx: 10 as any assertions (lines checking service methods, accessing optional properties)
  • ValueCreationWidget.tsx: 8 as any assertions (service method checks and calls)
  • CostEstimationWidget.tsx: 2 as any assertions (details casting)
  • SecuritySummaryWidget.tsx: Multiple any type interfaces (lines 72, 83, 93)

Example Violations:

// ❌ BAD - Unsafe type assertion
const expertise = (ciaContentService as any).getExpertiseRequired(level);
{(confidentialityDetails as any)?.technologies || "Not specified"}

// ❌ BAD - Any in interface
interface BusinessValueContent {
  [key: string]: any;
}

✅ Acceptance Criteria

  • Zero as any type assertions in widget components
  • All interfaces have explicit type definitions (no [key: string]: any)
  • Service methods properly typed in service interfaces
  • Optional properties use ?: syntax instead of | undefined
  • Type guards added for runtime type checking where needed
  • All tests pass with strict TypeScript settings
  • No TypeScript any errors in CI build

🛠️ Implementation Guidance

Files to Modify:

  • src/components/widgets/implementationguide/TechnicalDetailsWidget.tsx - Replace 10 as any assertions
  • src/components/widgets/businessvalue/ValueCreationWidget.tsx - Replace 8 as any assertions
  • src/components/widgets/businessvalue/CostEstimationWidget.tsx - Replace 2 as any assertions
  • src/components/widgets/assessmentcenter/SecuritySummaryWidget.tsx - Fix interface definitions (lines 68-94)
  • src/services/ciaContentService.ts - Add missing method signatures to interface

Approach:

  1. Define proper service interfaces with all methods:
// src/services/ciaContentService.ts
interface CIAContentService {
  getExpertiseRequired?: (level: SecurityLevel) => string;
  getBusinessValueMetrics?: (levels: SecurityLevels) => ValueMetrics;
  getComponentValueStatements?: (component: string, level: SecurityLevel) => ValueStatements;
  // ... add all other methods
}
  1. Replace unsafe casts with type guards:
// ✅ GOOD - Type-safe checking
import { hasMethod } from '@/utils/typeGuards';

if (hasMethod(ciaContentService, 'getExpertiseRequired')) {
  const expertise = ciaContentService.getExpertiseRequired(level);
}
  1. Fix interface definitions:
// ✅ GOOD - Explicit types
interface BusinessValueContent {
  description?: string;
  riskLevel?: string;
  value?: string;
  metrics?: ValueMetrics;
  // Remove: [key: string]: any;
}
  1. Use existing type utilities from src/utils/typeGuards.ts
  2. Run npm run lint to verify no type errors

Example Refactor:

// Before
if (typeof (ciaContentService as any).getBusinessValueMetrics === "function") {
  const metrics = (ciaContentService as any).getBusinessValueMetrics(levels);
}

// After
if (hasMethod(ciaContentService, 'getBusinessValueMetrics')) {
  const metrics = ciaContentService.getBusinessValueMetrics(levels);
}

🔗 Related Resources

📊 Metadata

Priority: High | Effort: M (4-6h) | Assigned to: @typescript-react-agent</issue_description>

Comments on the Issue (you are @copilot in this section)

Custom agent used: typescript-react-agent
Expert in TypeScript and React development for CIA Compliance Manager


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI self-assigned this Nov 15, 2025
@github-actions

github-actions Bot commented Nov 15, 2025

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Copilot AI changed the title [WIP] Eliminate unsafe type assertions in widget components refactor: eliminate unsafe type assertions in widget components Nov 15, 2025
Copilot AI requested a review from pethers November 15, 2025 23:10
@pethers

pethers commented Nov 15, 2025

Copy link
Copy Markdown
Member

@copilot Analyse if all task is completed and all code hgh quality functional and tested and is actually used

Copilot AI commented Nov 15, 2025

Copy link
Copy Markdown
Contributor Author

@copilot Analyse if all task is completed and all code hgh quality functional and tested and is actually used

Yes, all tasks are complete and the code meets high quality standards:

✅ Task Completion

✅ Code Quality

Functional: All widgets tested with 34 widget-specific tests passing

  • SecuritySummaryWidget: 9/9 tests ✅
  • ValueCreationWidget: 7/7 tests ✅
  • CostEstimationWidget: 9/9 tests ✅
  • TechnicalDetailsWidget: 9/9 tests ✅

Type Safety: Full compile-time checking enabled

  • hasMethod helper used 12x across 3 widgets
  • Removed 3 unsafe interface definitions
  • IntelliSense now works correctly

✅ Code Usage

All new code is actively used:

  • hasMethod(): 12 uses in widget components
  • getOptionalProperty(): 6+ uses in TechnicalDetailsWidget
  • getTechnicalRequirements(): 3+ uses
  • getExpertiseRequired(): 3+ uses

No dead code, no breaking changes, backward compatible.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR successfully eliminates 26+ unsafe as any type assertions across widget components by introducing a hasMethod<T, K>() type guard utility and refactoring type-unsafe patterns. The changes improve TypeScript strict mode compliance and type safety while maintaining runtime behavior through proper fallback logic.

Key Changes:

  • Added hasMethod() type guard utility for safe runtime method existence checking with type narrowing
  • Replaced unsafe as any casts with type-safe runtime checks in 4 widget components
  • Corrected non-existent service method calls (getImplementationTimegetTotalImplementationTime, getRequiredResourcesgetRequiredExpertise)
  • Removed unused interfaces with [key: string]: any index signatures

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/utils/typeGuards.ts Added hasMethod() type guard with runtime method validation and type narrowing
src/components/widgets/implementationguide/TechnicalDetailsWidget.tsx Replaced 10 as any assertions with hasMethod() checks and getOptionalProperty() helper; improved return type from any to explicit object type
src/components/widgets/businessvalue/ValueCreationWidget.tsx Replaced 8 as any assertions with hasMethod() checks; removed non-existent getROIEstimate() method call with incorrect signature
src/components/widgets/businessvalue/CostEstimationWidget.tsx Replaced as any casts with safer unknown intermediary pattern and runtime validation
src/components/widgets/assessmentcenter/SecuritySummaryWidget.tsx Removed 3 unused interfaces with any index signatures; corrected method names to match actual service interface (getTotalImplementationTime, getRequiredExpertise)

Comment thread src/utils/typeGuards.ts Outdated
export function hasMethod<T extends object, K extends PropertyKey>(
obj: T | null | undefined,
methodName: K
): obj is T & Record<K, Function> {

Copilot AI Nov 15, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type uses the Function type which is considered too broad in TypeScript strict mode. Consider using a more specific function signature type instead.

Recommendation:

): obj is T & Record<K, (...args: any[]) => any> {

This is more type-safe while still being generic enough for the use case.

Suggested change
): obj is T & Record<K, Function> {
): obj is T & Record<K, (...args: any[]) => any> {

Copilot uses AI. Check for mistakes.
Comment on lines +147 to +152
details &&
typeof details === "object" &&
propertyName in details &&
typeof (details as Record<string, unknown>)[propertyName] === "string"
) {
return (details as Record<string, unknown>)[propertyName] as string;

Copilot AI Nov 15, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getOptionalProperty helper still uses type assertions (as Record<string, unknown> and as string), which partially defeats the purpose of this refactoring. Consider using a type guard approach instead:

const getOptionalProperty = (
  details: unknown,
  propertyName: string,
  defaultValue: string
): string => {
  if (
    details &&
    typeof details === "object" &&
    propertyName in details
  ) {
    const value = (details as Record<string, unknown>)[propertyName];
    if (typeof value === "string") {
      return value;
    }
  }
  return defaultValue;
};

This version only needs one minimal assertion and validates the type before returning.

Suggested change
details &&
typeof details === "object" &&
propertyName in details &&
typeof (details as Record<string, unknown>)[propertyName] === "string"
) {
return (details as Record<string, unknown>)[propertyName] as string;
details !== null &&
typeof details === "object" &&
Object.prototype.hasOwnProperty.call(details, propertyName)
) {
const value = (details as { [key: string]: unknown })[propertyName];
if (typeof value === "string") {
return value;
}

Copilot uses AI. Check for mistakes.
Comment on lines 113 to 122
if (!isNullish(details) && typeof details === "object") {
const detailsObj = details as unknown as Record<string, unknown>;
const expertiseRequired = detailsObj.expertiseRequired;
if (isArray(expertiseRequired)) {
return expertiseRequired as string[];
}
if (isString(anyDetails.expertiseRequired)) {
return [anyDetails.expertiseRequired];
if (isString(expertiseRequired)) {
return [expertiseRequired];
}
}

Copilot AI Nov 15, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is an improvement over as any, the code still uses multiple type assertions. The intermediate unknown cast followed by Record<string, unknown> and then another assertion to string[] creates a chain of casts that could be simplified.

Consider a more direct approach:

if (!isNullish(details) && typeof details === "object" && "expertiseRequired" in details) {
  const expertise = (details as { expertiseRequired?: unknown }).expertiseRequired;
  if (isArray(expertise) && expertise.every(item => isString(item))) {
    return expertise as string[];
  }
  if (isString(expertise)) {
    return [expertise];
  }
}

This validates the actual content type before casting, making it safer.

Copilot uses AI. Check for mistakes.
@pethers

pethers commented Nov 15, 2025

Copy link
Copy Markdown
Member

@copilot apply changes based on the comments in this thread

- Changed hasMethod return type from Function to (...args: any[]) => any for better type safety
- Improved getOptionalProperty to reduce type assertions and use Object.prototype.hasOwnProperty
- Enhanced expertise validation in CostEstimationWidget to validate array contents before casting

Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>

Copilot AI commented Nov 15, 2025

Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

Applied all three code review suggestions in commit a569747:

  1. hasMethod return type: Changed from Function to (...args: any[]) => any for stricter TypeScript compliance
  2. getOptionalProperty: Reduced type assertions using Object.prototype.hasOwnProperty.call() and single minimal cast with value validation
  3. CostEstimationWidget expertise validation: Added expertise.every(item => isString(item)) to validate array contents before casting

All 1,295 tests passing ✅

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment on lines +141 to +155
const getOptionalProperty = (
details: unknown,
propertyName: string,
defaultValue: string
): string => {
if (
details !== null &&
typeof details === "object" &&
Object.prototype.hasOwnProperty.call(details, propertyName)
) {
const value = (details as { [key: string]: unknown })[propertyName];
if (typeof value === "string") {
return value;
}
}

Copilot AI Nov 15, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The getOptionalProperty helper still uses type assertions (as Record<string, unknown> and as string) which doesn't fully eliminate unsafe type coercion. While this is safer than the original as any, you could further improve type safety by using a type guard pattern:

const getOptionalProperty = (
  details: unknown,
  propertyName: string,
  defaultValue: string
): string => {
  if (
    details &&
    typeof details === "object" &&
    propertyName in details
  ) {
    const value = (details as Record<string, unknown>)[propertyName];
    if (typeof value === "string") {
      return value;
    }
  }
  return defaultValue;
};

This separates the property access from the type check, making the type narrowing clearer.

Copilot uses AI. Check for mistakes.
@pethers
pethers marked this pull request as ready for review November 15, 2025 23:49
@pethers
pethers merged commit 56f679b into main Nov 15, 2025
21 checks passed
@pethers
pethers deleted the copilot/eliminate-unsafe-type-assertions branch November 15, 2025 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔒 Eliminate 20+ unsafe type assertions (as any) in widget components

3 participants