Automatic quality control, linting, and static analysis procedures. Use after every code modification to ensure syntax correctness and project standards. Triggers onKeywords: lint, format, check, validate, types, static analysis.
89
89%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
#!/usr/bin/env python3
"""
Type Coverage Checker - Measures TypeScript/Python type coverage.
Identifies untyped functions, any usage, and type safety issues.
"""
import sys
import re
import subprocess
from pathlib import Path
# Fix Windows console encoding for Unicode output
try:
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
except AttributeError:
pass # Python < 3.7
def check_typescript_coverage(project_path: Path) -> dict:
"""Check TypeScript type coverage."""
issues = []
passed = []
stats = {'any_count': 0, 'untyped_functions': 0, 'total_functions': 0}
ts_files = list(project_path.rglob("*.ts")) + list(project_path.rglob("*.tsx"))
ts_files = [f for f in ts_files if 'node_modules' not in str(f) and '.d.ts' not in str(f)]
if not ts_files:
return {'type': 'typescript', 'files': 0, 'passed': [], 'issues': ["[!] No TypeScript files found"], 'stats': stats}
for file_path in ts_files[:30]: # Limit
try:
content = file_path.read_text(encoding='utf-8', errors='ignore')
# Count 'any' usage
any_matches = re.findall(r':\s*any\b', content)
stats['any_count'] += len(any_matches)
# Find functions without return types
# function name(params) { - no return type
untyped = re.findall(r'function\s+\w+\s*\([^)]*\)\s*{', content)
# Arrow functions without types: const fn = (x) => or (x) =>
untyped += re.findall(r'=\s*\([^:)]*\)\s*=>', content)
stats['untyped_functions'] += len(untyped)
# Count typed functions
typed = re.findall(r'function\s+\w+\s*\([^)]*\)\s*:\s*\w+', content)
typed += re.findall(r':\s*\([^)]*\)\s*=>\s*\w+', content)
stats['total_functions'] += len(typed) + len(untyped)
except Exception:
continue
# Analyze results
if stats['any_count'] == 0:
passed.append("[OK] No 'any' types found")
elif stats['any_count'] <= 5:
issues.append(f"[!] {stats['any_count']} 'any' types found (acceptable)")
else:
issues.append(f"[X] {stats['any_count']} 'any' types found (too many)")
if stats['total_functions'] > 0:
typed_ratio = (stats['total_functions'] - stats['untyped_functions']) / stats['total_functions'] * 100
if typed_ratio >= 80:
passed.append(f"[OK] Type coverage: {typed_ratio:.0f}%")
elif typed_ratio >= 50:
issues.append(f"[!] Type coverage: {typed_ratio:.0f}% (improve)")
else:
issues.append(f"[X] Type coverage: {typed_ratio:.0f}% (too low)")
passed.append(f"[OK] Analyzed {len(ts_files)} TypeScript files")
return {'type': 'typescript', 'files': len(ts_files), 'passed': passed, 'issues': issues, 'stats': stats}
def check_python_coverage(project_path: Path) -> dict:
"""Check Python type hints coverage."""
issues = []
passed = []
stats = {'untyped_functions': 0, 'typed_functions': 0, 'any_count': 0}
py_files = list(project_path.rglob("*.py"))
py_files = [f for f in py_files if not any(x in str(f) for x in ['venv', '__pycache__', '.git', 'node_modules'])]
if not py_files:
return {'type': 'python', 'files': 0, 'passed': [], 'issues': ["[!] No Python files found"], 'stats': stats}
for file_path in py_files[:30]: # Limit
try:
content = file_path.read_text(encoding='utf-8', errors='ignore')
# Count Any usage
any_matches = re.findall(r':\s*Any\b', content)
stats['any_count'] += len(any_matches)
# Find functions with type hints
typed_funcs = re.findall(r'def\s+\w+\s*\([^)]*:[^)]+\)', content)
typed_funcs += re.findall(r'def\s+\w+\s*\([^)]*\)\s*->', content)
stats['typed_functions'] += len(typed_funcs)
# Find functions without type hints
all_funcs = re.findall(r'def\s+\w+\s*\(', content)
stats['untyped_functions'] += len(all_funcs) - len(typed_funcs)
except Exception:
continue
total = stats['typed_functions'] + stats['untyped_functions']
if total > 0:
typed_ratio = stats['typed_functions'] / total * 100
if typed_ratio >= 70:
passed.append(f"[OK] Type hints coverage: {typed_ratio:.0f}%")
elif typed_ratio >= 40:
issues.append(f"[!] Type hints coverage: {typed_ratio:.0f}%")
else:
issues.append(f"[X] Type hints coverage: {typed_ratio:.0f}% (add type hints)")
if stats['any_count'] == 0:
passed.append("[OK] No 'Any' types found")
elif stats['any_count'] <= 3:
issues.append(f"[!] {stats['any_count']} 'Any' types found")
else:
issues.append(f"[X] {stats['any_count']} 'Any' types found")
passed.append(f"[OK] Analyzed {len(py_files)} Python files")
return {'type': 'python', 'files': len(py_files), 'passed': passed, 'issues': issues, 'stats': stats}
def main():
target = sys.argv[1] if len(sys.argv) > 1 else "."
project_path = Path(target)
print("\n" + "=" * 60)
print(" TYPE COVERAGE CHECKER")
print("=" * 60 + "\n")
results = []
# Check TypeScript
ts_result = check_typescript_coverage(project_path)
if ts_result['files'] > 0:
results.append(ts_result)
# Check Python
py_result = check_python_coverage(project_path)
if py_result['files'] > 0:
results.append(py_result)
if not results:
print("[!] No TypeScript or Python files found.")
sys.exit(0)
# Print results
critical_issues = 0
for result in results:
print(f"\n[{result['type'].upper()}]")
print("-" * 40)
for item in result['passed']:
print(f" {item}")
for item in result['issues']:
print(f" {item}")
if item.startswith("[X]"):
critical_issues += 1
print("\n" + "=" * 60)
if critical_issues == 0:
print("[OK] TYPE COVERAGE: ACCEPTABLE")
sys.exit(0)
else:
print(f"[X] TYPE COVERAGE: {critical_issues} critical issues")
sys.exit(1)
if __name__ == "__main__":
main()