Skip to content

Commit 41181c9

Browse files
authored
Redesign tool config types to avoid any typing (Stirling-Tools#6582)
# Description of Changes Fixes one of the main causes of `any` typing left in tools, the way that we register tool parameters in the registry. Currently, it just accepts tool params via `any`, but instead we can explicitly change them to `Record<string, unknown)`, so on the way back out they can more safely be cast back to their correct type when known. One consequence of this is that I had to redesign the way we special-case the Convert tool, which previously was a different shape than all the other param types. Now it's just got optional parameters on it, which isn't quite as type-safe as before, but it does mean all tools are a consistent shape now, which I think is worth the tradeoff.
1 parent 8e48580 commit 41181c9

12 files changed

Lines changed: 187 additions & 127 deletions

File tree

frontend/editor/src/core/components/tools/automate/ToolConfigurationModal.tsx

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@ import CloseIcon from "@mui/icons-material/Close";
1717
import WarningIcon from "@mui/icons-material/Warning";
1818
import { ToolRegistry } from "@app/data/toolsTaxonomy";
1919
import { ToolId } from "@app/types/toolId";
20-
import { getAvailableToExtensions } from "@app/utils/convertUtils";
20+
import { ErasedToolParams } from "@app/hooks/tools/shared/toolOperationTypes";
2121
interface ToolConfigurationModalProps {
2222
opened: boolean;
2323
tool: {
2424
id: string;
2525
operation: string;
2626
name: string;
27-
parameters?: any;
27+
parameters?: ErasedToolParams;
2828
};
29-
onSave: (parameters: any) => void;
29+
onSave: (parameters: ErasedToolParams) => void;
3030
onCancel: () => void;
3131
toolRegistry: Partial<ToolRegistry>;
3232
}
@@ -40,7 +40,7 @@ export default function ToolConfigurationModal({
4040
}: ToolConfigurationModalProps) {
4141
const { t } = useTranslation();
4242

43-
const [parameters, setParameters] = useState<any>({});
43+
const [parameters, setParameters] = useState<ErasedToolParams>({});
4444

4545
// Get tool info from registry
4646
const toolInfo = toolRegistry[tool.operation as ToolId];
@@ -74,26 +74,11 @@ export default function ToolConfigurationModal({
7474
);
7575
}
7676

77-
// Special handling for ConvertSettings which needs additional props
78-
if (tool.operation === "convert") {
79-
return (
80-
<SettingsComponent
81-
parameters={parameters}
82-
onParameterChange={(key: string, value: any) => {
83-
setParameters((prev: any) => ({ ...prev, [key]: value }));
84-
}}
85-
getAvailableToExtensions={getAvailableToExtensions}
86-
selectedFiles={[]}
87-
disabled={false}
88-
/>
89-
);
90-
}
91-
9277
return (
9378
<SettingsComponent
9479
parameters={parameters}
95-
onParameterChange={(key: string, value: any) => {
96-
setParameters((prev: any) => ({ ...prev, [key]: value }));
80+
onParameterChange={(key, value) => {
81+
setParameters((prev) => ({ ...prev, [key]: value }));
9782
}}
9883
disabled={false}
9984
/>

frontend/editor/src/core/components/tools/automate/ToolList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ interface ToolListProps {
1818
onToolConfigure: (index: number) => void;
1919
onToolAdd: () => void;
2020
getToolName: (operation: string) => string;
21-
getToolDefaultParameters: (operation: string) => Record<string, any>;
21+
getToolDefaultParameters: (operation: string) => Record<string, unknown>;
2222
}
2323

2424
export default function ToolList({

frontend/editor/src/core/components/tools/convert/ConvertSettings.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import {
1010
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
1111
import { useTranslation } from "react-i18next";
1212
import { useMultipleEndpointsEnabled } from "@app/hooks/useEndpointConfig";
13-
import { isImageFormat, isWebFormat } from "@app/utils/convertUtils";
13+
import {
14+
isImageFormat,
15+
isWebFormat,
16+
getAvailableToExtensions as defaultGetAvailableToExtensions,
17+
} from "@app/utils/convertUtils";
1418
import { getConversionEndpoints } from "@app/data/toolsTaxonomy";
1519
import { useFileSelection } from "@app/contexts/FileContext";
1620
import { useFileState } from "@app/contexts/FileContext";
@@ -47,18 +51,18 @@ interface ConvertSettingsProps {
4751
key: K,
4852
value: ConvertParameters[K],
4953
) => void;
50-
getAvailableToExtensions: (
54+
getAvailableToExtensions?: (
5155
fromExtension: string,
5256
) => Array<{ value: string; label: string; group: string }>;
53-
selectedFiles: StirlingFile[];
57+
selectedFiles?: StirlingFile[];
5458
disabled?: boolean;
5559
}
5660

5761
const ConvertSettings = ({
5862
parameters,
5963
onParameterChange,
60-
getAvailableToExtensions,
61-
selectedFiles,
64+
getAvailableToExtensions = defaultGetAvailableToExtensions,
65+
selectedFiles = [],
6266
disabled = false,
6367
}: ConvertSettingsProps) => {
6468
const { t } = useTranslation();

frontend/editor/src/core/components/tools/shared/FilesToolStep.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,20 @@ export interface FilesToolStepProps {
1010
minFiles?: number;
1111
}
1212

13-
export function createFilesToolStep(
13+
interface StepBaseProps {
14+
isVisible?: boolean;
15+
isCollapsed?: boolean;
16+
onCollapsedClick?: () => void;
17+
}
18+
19+
export function createFilesToolStep<T>(
1420
createStep: (
1521
title: string,
16-
props: any,
22+
props: StepBaseProps,
1723
children?: React.ReactNode,
18-
) => React.ReactElement,
24+
) => T,
1925
props: FilesToolStepProps,
20-
): React.ReactElement {
26+
): T {
2127
return createStep(
2228
i18n.t("files.title", "Files"),
2329
{

frontend/editor/src/core/data/toolsTaxonomy.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { type TFunction } from "i18next";
22
import React from "react";
3-
import { ToolOperationConfig } from "@app/hooks/tools/shared/toolOperationTypes";
3+
import {
4+
type ErasedToolParams,
5+
type ToolAutomationSettingsProps,
6+
ToolOperationConfig,
7+
} from "@app/hooks/tools/shared/toolOperationTypes";
48
import { BaseToolProps } from "@app/types/tool";
59
import { WorkbenchType } from "@app/types/workbench";
610
import {
@@ -58,10 +62,13 @@ export type ToolRegistryEntry = {
5862
kind?: ToolKind;
5963
// Workbench type for navigation
6064
workbench?: WorkbenchType;
61-
// Operation configuration for automation
62-
operationConfig?: ToolOperationConfig<any>;
63-
// Settings component for automation configuration
64-
automationSettings: React.ComponentType<any> | null;
65+
// Operation configuration for automation. TParams is erased at the registry
66+
// boundary; tools are authored type-safely via defineToolAutomation.
67+
operationConfig?: ToolOperationConfig<ErasedToolParams>;
68+
// Settings component for automation configuration.
69+
automationSettings: React.ComponentType<
70+
ToolAutomationSettingsProps<ErasedToolParams>
71+
> | null;
6572
// Whether this tool supports automation (defaults to true)
6673
supportsAutomate?: boolean;
6774
// Synonyms for search (optional)

0 commit comments

Comments
 (0)