fix: replace as-unknown-as casts with safer patterns (#9107)

## Summary

- Replace 83 `as unknown as` double casts with safer alternatives across
33 files
- Use `as Partial<X> as X` pattern where TypeScript allows it
- Create/reuse factory functions from `litegraphTestUtils.ts` for mock
objects
- Widen `getWorkflowDataFromFile` return type to include `ComfyMetadata`
directly
- Reduce total `as unknown as` count from ~153 to 71

The remaining 71 occurrences are genuinely necessary due to cross-schema
casts, generic variance, missing index signatures, Float64Array-to-tuple
conversions, and DOM type incompatibilities.

## Test plan

- [x] `pnpm typecheck` passes
- [x] `pnpm lint` passes
- [x] All affected unit tests pass

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9107-fix-replace-as-unknown-as-casts-with-safer-patterns-3106d73d3650815cb5bcd613ad635bd7)
by [Unito](https://www.unito.io)
This commit is contained in:
Johnpaul Chiwetelu
2026-02-23 05:46:12 +01:00
committed by GitHub
parent 8998d92e1b
commit 02e926471f
33 changed files with 282 additions and 346 deletions

View File

@@ -72,7 +72,9 @@ describe('useNodePreviewAndDrag', () => {
toJSON: () => ({})
})
const mockEvent = { currentTarget: mockElement } as unknown as MouseEvent
const mockEvent = {
currentTarget: mockElement
} as Partial<MouseEvent> as MouseEvent
result.handleMouseEnter(mockEvent)
expect(result.isHovered.value).toBe(true)
@@ -83,7 +85,9 @@ describe('useNodePreviewAndDrag', () => {
const result = useNodePreviewAndDrag(nodeDef)
const mockElement = document.createElement('div')
const mockEvent = { currentTarget: mockElement } as unknown as MouseEvent
const mockEvent = {
currentTarget: mockElement
} as Partial<MouseEvent> as MouseEvent
result.handleMouseEnter(mockEvent)
expect(result.isHovered.value).toBe(false)
@@ -150,7 +154,7 @@ describe('useNodePreviewAndDrag', () => {
const mockEvent = {
clientX: 100,
clientY: 200
} as unknown as DragEvent
} as Partial<DragEvent> as DragEvent
result.handleDragEnd(mockEvent)
@@ -168,7 +172,7 @@ describe('useNodePreviewAndDrag', () => {
dataTransfer: { dropEffect: 'none' },
clientX: 300,
clientY: 400
} as unknown as DragEvent
} as Partial<DragEvent> as DragEvent
result.handleDragEnd(mockEvent)

View File

@@ -3,7 +3,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { nextTick, reactive } from 'vue'
import { useQueueNotificationBanners } from '@/composables/queue/useQueueNotificationBanners'
import { api } from '@/scripts/api'
import { useExecutionStore } from '@/stores/executionStore'
import { useQueueStore } from '@/stores/queueStore'
@@ -135,7 +134,7 @@ describe(useQueueNotificationBanners, () => {
const { wrapper, composable } = mountComposable()
try {
;(api as unknown as EventTarget).dispatchEvent(
mockApi.dispatchEvent(
new CustomEvent('promptQueued', { detail: { batchCount: 4 } })
)
await nextTick()
@@ -157,7 +156,7 @@ describe(useQueueNotificationBanners, () => {
const { wrapper, composable } = mountComposable()
try {
;(api as unknown as EventTarget).dispatchEvent(
mockApi.dispatchEvent(
new CustomEvent('promptQueueing', {
detail: { requestId: 1, batchCount: 2 }
})
@@ -170,7 +169,7 @@ describe(useQueueNotificationBanners, () => {
requestId: 1
})
;(api as unknown as EventTarget).dispatchEvent(
mockApi.dispatchEvent(
new CustomEvent('promptQueued', {
detail: { requestId: 1, batchCount: 2 }
})
@@ -191,7 +190,7 @@ describe(useQueueNotificationBanners, () => {
const { wrapper, composable } = mountComposable()
try {
;(api as unknown as EventTarget).dispatchEvent(
mockApi.dispatchEvent(
new CustomEvent('promptQueued', { detail: { batchCount: 0 } })
)
await nextTick()

View File

@@ -7,6 +7,7 @@ import type {
} from '@/lib/litegraph/src/litegraph'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import { app } from '@/scripts/app'
import { createMockLGraphNode } from '@/utils/__tests__/litegraphTestUtils'
import { createNode, isImageNode } from '@/utils/litegraphUtil'
import {
cloneDataTransfer,
@@ -15,12 +16,12 @@ import {
usePaste
} from './usePaste'
function createMockNode() {
return {
function createMockNode(): LGraphNode {
return createMockLGraphNode({
pos: [0, 0],
pasteFile: vi.fn(),
pasteFiles: vi.fn()
}
})
}
function createImageFile(
@@ -84,7 +85,8 @@ vi.mock('@/scripts/app', () => ({
}
}))
vi.mock('@/lib/litegraph/src/litegraph', () => ({
vi.mock('@/lib/litegraph/src/litegraph', async (importOriginal) => ({
...(await importOriginal()),
LiteGraph: {
createNode: vi.fn()
}
@@ -111,15 +113,12 @@ describe('pasteImageNode', () => {
it('should create new LoadImage node when no image node provided', async () => {
const mockNode = createMockNode()
vi.mocked(createNode).mockResolvedValue(mockNode as unknown as LGraphNode)
vi.mocked(createNode).mockResolvedValue(mockNode)
const file = createImageFile()
const dataTransfer = createDataTransfer([file])
await pasteImageNode(
mockCanvas as unknown as LGraphCanvas,
dataTransfer.items
)
await pasteImageNode(mockCanvas, dataTransfer.items)
expect(createNode).toHaveBeenCalledWith(mockCanvas, 'LoadImage')
expect(mockNode.pasteFile).toHaveBeenCalledWith(file)
@@ -130,11 +129,7 @@ describe('pasteImageNode', () => {
const file = createImageFile()
const dataTransfer = createDataTransfer([file])
await pasteImageNode(
mockCanvas as unknown as LGraphCanvas,
dataTransfer.items,
mockNode as unknown as LGraphNode
)
await pasteImageNode(mockCanvas, dataTransfer.items, mockNode)
expect(mockNode.pasteFile).toHaveBeenCalledWith(file)
expect(mockNode.pasteFiles).toHaveBeenCalledWith([file])
@@ -146,11 +141,7 @@ describe('pasteImageNode', () => {
const file2 = createImageFile('test2.jpg', 'image/jpeg')
const dataTransfer = createDataTransfer([file1, file2])
await pasteImageNode(
mockCanvas as unknown as LGraphCanvas,
dataTransfer.items,
mockNode as unknown as LGraphNode
)
await pasteImageNode(mockCanvas, dataTransfer.items, mockNode)
expect(mockNode.pasteFile).toHaveBeenCalledWith(file1)
expect(mockNode.pasteFiles).toHaveBeenCalledWith([file1, file2])
@@ -160,11 +151,7 @@ describe('pasteImageNode', () => {
const mockNode = createMockNode()
const dataTransfer = createDataTransfer()
await pasteImageNode(
mockCanvas as unknown as LGraphCanvas,
dataTransfer.items,
mockNode as unknown as LGraphNode
)
await pasteImageNode(mockCanvas, dataTransfer.items, mockNode)
expect(mockNode.pasteFile).not.toHaveBeenCalled()
expect(mockNode.pasteFiles).not.toHaveBeenCalled()
@@ -176,11 +163,7 @@ describe('pasteImageNode', () => {
const textFile = new File([''], 'test.txt', { type: 'text/plain' })
const dataTransfer = createDataTransfer([textFile, imageFile])
await pasteImageNode(
mockCanvas as unknown as LGraphCanvas,
dataTransfer.items,
mockNode as unknown as LGraphNode
)
await pasteImageNode(mockCanvas, dataTransfer.items, mockNode)
expect(mockNode.pasteFile).toHaveBeenCalledWith(imageFile)
expect(mockNode.pasteFiles).toHaveBeenCalledWith([imageFile])
@@ -196,16 +179,13 @@ describe('pasteImageNodes', () => {
const mockNode1 = createMockNode()
const mockNode2 = createMockNode()
vi.mocked(createNode)
.mockResolvedValueOnce(mockNode1 as unknown as LGraphNode)
.mockResolvedValueOnce(mockNode2 as unknown as LGraphNode)
.mockResolvedValueOnce(mockNode1)
.mockResolvedValueOnce(mockNode2)
const file1 = createImageFile('test1.png')
const file2 = createImageFile('test2.jpg', 'image/jpeg')
const result = await pasteImageNodes(
mockCanvas as unknown as LGraphCanvas,
[file1, file2]
)
const result = await pasteImageNodes(mockCanvas, [file1, file2])
expect(createNode).toHaveBeenCalledTimes(2)
expect(createNode).toHaveBeenNthCalledWith(1, mockCanvas, 'LoadImage')
@@ -216,10 +196,7 @@ describe('pasteImageNodes', () => {
})
it('should handle empty file list', async () => {
const result = await pasteImageNodes(
mockCanvas as unknown as LGraphCanvas,
[]
)
const result = await pasteImageNodes(mockCanvas, [])
expect(createNode).not.toHaveBeenCalled()
expect(result).toEqual([])
@@ -238,7 +215,7 @@ describe('usePaste', () => {
it('should handle image paste', async () => {
const mockNode = createMockNode()
vi.mocked(createNode).mockResolvedValue(mockNode as unknown as LGraphNode)
vi.mocked(createNode).mockResolvedValue(mockNode)
usePaste()
@@ -255,9 +232,7 @@ describe('usePaste', () => {
it('should handle audio paste', async () => {
const mockNode = createMockNode()
vi.mocked(LiteGraph.createNode).mockReturnValue(
mockNode as unknown as LGraphNode
)
vi.mocked(LiteGraph.createNode).mockReturnValue(mockNode)
usePaste()
@@ -302,11 +277,11 @@ describe('usePaste', () => {
})
it('should use existing image node when selected', () => {
const mockNode = {
const mockNode = createMockLGraphNode({
is_selected: true,
pasteFile: vi.fn(),
pasteFiles: vi.fn()
} as unknown as Partial<LGraphNode> as LGraphNode
})
mockCanvas.current_node = mockNode
vi.mocked(isImageNode).mockReturnValue(true)