feat: No Explicit Any (#8601)

## Summary
- Add `typescript/no-explicit-any` rule to `.oxlintrc.json` to enforce
no explicit `any` types
- Fix all 40 instances of explicit `any` throughout the codebase
- Improve type safety with proper TypeScript types

## Changes Made

### Configuration
- Added `typescript/no-explicit-any` rule to `.oxlintrc.json`

### Type Fixes
- Replaced `any` with `unknown` for truly unknown types
- Updated generic type parameters to use `unknown` defaults instead of
`any`
- Fixed method `this` parameters to avoid variance issues
- Updated component props to match new generic types
- Fixed test mocks to use proper type assertions

### Key Files Modified
- `src/types/treeExplorerTypes.ts`: Updated TreeExplorerNode interface
generics
- `src/platform/settings/types.ts`: Fixed SettingParams generic default
- `src/lib/litegraph/src/LGraph.ts`: Fixed ParamsArray type constraint
- `src/extensions/core/electronAdapter.ts`: Fixed onChange callbacks
- `src/views/GraphView.vue`: Added proper type imports
- Multiple test files: Fixed type assertions and mocks

## Test Plan
- [x] All lint checks pass (`pnpm lint`)
- [x] TypeScript compilation succeeds (`pnpm typecheck`)
- [x] Pre-commit hooks pass
- [x] No regression in functionality

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8601-feat-add-typescript-no-explicit-any-rule-and-fix-all-instances-2fd6d73d365081fd9beef75d5a6daf5b)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
This commit is contained in:
Johnpaul Chiwetelu
2026-02-12 00:13:48 +01:00
committed by GitHub
parent 92b7437d86
commit 4fc1d2ef5b
28 changed files with 242 additions and 151 deletions

View File

@@ -137,32 +137,40 @@ export const useExtensionService = () => {
}
}
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends (...args: unknown[]) => unknown ? K : never
}[keyof T]
type RemoveLastAppParam<T> = T extends (
...args: [...infer Rest, ComfyApp]
) => infer R
? (...args: Rest) => R
: T
type ComfyExtensionParamsWithoutApp<T extends keyof ComfyExtension> =
RemoveLastAppParam<ComfyExtension[T]>
type KnownExtensionMethods = Exclude<keyof ComfyExtension, number | symbol> &
string
type ComfyExtensionMethod<T extends KnownExtensionMethods> =
ComfyExtension[T] extends (...args: unknown[]) => unknown
? ComfyExtension[T]
: (...args: unknown[]) => unknown
type ComfyExtensionParamsWithoutApp<T extends KnownExtensionMethods> =
RemoveLastAppParam<ComfyExtensionMethod<T>>
/**
* Invoke an extension callback
* @param {keyof ComfyExtension} method The extension callback to execute
* @param {unknown[]} args Any arguments to pass to the callback
* @returns
*/
const invokeExtensions = <T extends FunctionPropertyNames<ComfyExtension>>(
const invokeExtensions = <T extends KnownExtensionMethods>(
method: T,
...args: Parameters<ComfyExtensionParamsWithoutApp<T>>
) => {
const results: ReturnType<ComfyExtension[T]>[] = []
const results: ReturnType<ComfyExtensionMethod<T>>[] = []
for (const ext of extensionStore.enabledExtensions) {
if (method in ext) {
try {
results.push(ext[method](...args, app))
const fn = ext[method]
if (typeof fn === 'function') {
results.push(fn.call(ext, ...args, app))
}
} catch (error) {
console.error(
`Error calling extension '${ext.name}' method '${method}'`,
@@ -183,9 +191,7 @@ export const useExtensionService = () => {
* @param {...unknown} args Any arguments to pass to the callback
* @returns
*/
const invokeExtensionsAsync = async <
T extends FunctionPropertyNames<ComfyExtension>
>(
const invokeExtensionsAsync = async <T extends KnownExtensionMethods>(
method: T,
...args: Parameters<ComfyExtensionParamsWithoutApp<T>>
) => {
@@ -193,12 +199,17 @@ export const useExtensionService = () => {
extensionStore.enabledExtensions.map(async (ext) => {
if (method in ext) {
try {
const fn = ext[method]
if (typeof fn !== 'function') {
return
}
// Set current extension name for legacy compatibility tracking
if (method === 'setup') {
legacyMenuCompat.setCurrentExtension(ext.name)
}
const result = await ext[method](...args, app)
const result = await fn.call(ext, ...args, app)
// Clear current extension after setup
if (method === 'setup') {