From 8698d4f0e6bbe67df525afc4757613017e5ceda2 Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Sat, 27 Dec 2025 00:48:30 +0800 Subject: [PATCH] type safe --- web/app/components/workflow/nodes/constants.ts | 8 ++++---- .../components/condition-list/condition-item.tsx | 6 +++--- .../components/condition-list/condition-operator.tsx | 4 ++-- .../nodes/if-else/components/condition-value.tsx | 4 ++-- .../workflow/nodes/iteration/use-interactions.ts | 2 +- .../metadata/condition-list/condition-operator.tsx | 4 ++-- .../components/metadata/condition-list/utils.ts | 11 ++++++++--- .../list-operator/components/filter-condition.tsx | 4 ++-- .../nodes/llm/components/config-prompt-item.tsx | 2 +- .../loop/components/condition-files-list-value.tsx | 8 ++++---- .../loop/components/condition-list/condition-item.tsx | 4 ++-- .../components/condition-list/condition-operator.tsx | 4 ++-- .../nodes/loop/components/condition-value.tsx | 4 ++-- web/app/components/workflow/nodes/loop/default.ts | 10 ++++++++-- web/app/components/workflow/nodes/loop/utils.ts | 11 ++++++++--- 15 files changed, 51 insertions(+), 35 deletions(-) diff --git a/web/app/components/workflow/nodes/constants.ts b/web/app/components/workflow/nodes/constants.ts index 0fabe9d799..36e48d7c62 100644 --- a/web/app/components/workflow/nodes/constants.ts +++ b/web/app/components/workflow/nodes/constants.ts @@ -8,17 +8,17 @@ type OptionItem = { i18nKey: I18nKeysByPrefix<'workflow', 'nodes.ifElse.optionName.'> } -export const FILE_TYPE_OPTIONS: OptionItem[] = [ +export const FILE_TYPE_OPTIONS = [ { value: 'image', i18nKey: 'image' }, { value: 'document', i18nKey: 'doc' }, { value: 'audio', i18nKey: 'audio' }, { value: 'video', i18nKey: 'video' }, -] +] as const satisfies readonly OptionItem[] -export const TRANSFER_METHOD: OptionItem[] = [ +export const TRANSFER_METHOD = [ { value: TransferMethod.local_file, i18nKey: 'localUpload' }, { value: TransferMethod.remote_url, i18nKey: 'url' }, -] +] as const satisfies readonly OptionItem[] export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method', 'related_id'] export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method') diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx index 9d2993e2db..ebca9defe1 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx @@ -49,7 +49,7 @@ import ConditionInput from './condition-input' import ConditionOperator from './condition-operator' import ConditionVarSelector from './condition-var-selector' -const optionNameI18NPrefix = 'nodes.ifElse.optionName' +const optionNameI18NPrefix = 'nodes.ifElse.optionName' as const type ConditionItemProps = { className?: string @@ -168,13 +168,13 @@ const ConditionItem = ({ if (isSelect) { if (fileAttr?.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) { return FILE_TYPE_OPTIONS.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string, + name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }), value: item.value, })) } if (fileAttr?.key === 'transfer_method') { return TRANSFER_METHOD.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string, + name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }), value: item.value, })) } diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx index 3398f49b21..e6339b171e 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx @@ -39,7 +39,7 @@ const ConditionOperator = ({ const options = useMemo(() => { return getOperators(varType, file).map((o) => { return { - label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}` as any, { ns: 'workflow' }) as string : o, + label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`, { ns: 'workflow' }) : o, value: o, } }) @@ -65,7 +65,7 @@ const ConditionOperator = ({ { selectedOption ? selectedOption.label - : t(`${i18nPrefix}.select` as any, { ns: 'workflow' }) as string + : t(`${i18nPrefix}.select`, { ns: 'workflow' }) } diff --git a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx index 486ef3d814..6afa708494 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx @@ -35,7 +35,7 @@ const ConditionValue = ({ const { t } = useTranslation() const nodes = useNodes() const variableName = labelName || (isSystemVar(variableSelector) ? variableSelector.slice(0).join('.') : variableSelector.slice(1).join('.')) - const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`nodes.ifElse.comparisonOperator.${operator}` as any, { ns: 'workflow' }) as string : operator + const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`nodes.ifElse.comparisonOperator.${operator}`, { ns: 'workflow' }) : operator const notHasValue = comparisonOperatorNotRequireValue(operator) const node: Node | undefined = nodes.find(n => n.id === variableSelector[0]) as Node const isException = isExceptionVariable(variableName, node?.data.type) @@ -63,7 +63,7 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(value) ? value[0] : value))[0] return name - ? (t(`nodes.ifElse.optionName.${name.i18nKey}` as any, { ns: 'workflow' }) as string).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + ? t(`nodes.ifElse.optionName.${name.i18nKey}`, { ns: 'workflow' }).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` diff --git a/web/app/components/workflow/nodes/iteration/use-interactions.ts b/web/app/components/workflow/nodes/iteration/use-interactions.ts index 505dc2d6cb..678c71995e 100644 --- a/web/app/components/workflow/nodes/iteration/use-interactions.ts +++ b/web/app/components/workflow/nodes/iteration/use-interactions.ts @@ -135,7 +135,7 @@ export const useNodeIterationInteractions = () => { _isBundled: false, _connectedSourceHandleIds: [], _connectedTargetHandleIds: [], - title: nodesWithSameType.length > 0 ? `${t(`blocks.${childNodeType}` as any, { ns: 'workflow' }) as string} ${childNodeTypeCount[childNodeType]}` : t(`blocks.${childNodeType}` as any, { ns: 'workflow' }) as string, + title: nodesWithSameType.length > 0 ? `${t(`blocks.${childNodeType}`, { ns: 'workflow' })} ${childNodeTypeCount[childNodeType]}` : t(`blocks.${childNodeType}`, { ns: 'workflow' }), iteration_id: newNodeId, type: childNodeType, }, diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx index dad623ee1a..4af1bc00f8 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx @@ -42,7 +42,7 @@ const ConditionOperator = ({ const options = useMemo(() => { return getOperators(variableType).map((o) => { return { - label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}` as any, { ns: 'workflow' }) as string : o, + label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`, { ns: 'workflow' }) : o, value: o, } }) @@ -68,7 +68,7 @@ const ConditionOperator = ({ { selectedOption ? selectedOption.label - : t(`${i18nPrefix}.select` as any, { ns: 'workflow' }) as string + : t(`${i18nPrefix}.select`, { ns: 'workflow' }) } diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts index ab68275b8d..ff46c8ade1 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts @@ -14,12 +14,17 @@ const notTranslateKey = [ ComparisonOperator.largerThanOrEqual, ComparisonOperator.lessThan, ComparisonOperator.lessThanOrEqual, -] +] as const -export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => { +type NotTranslateOperator = typeof notTranslateKey[number] +export type TranslatableComparisonOperator = Exclude + +export function isComparisonOperatorNeedTranslate(operator: ComparisonOperator): operator is TranslatableComparisonOperator +export function isComparisonOperatorNeedTranslate(operator?: ComparisonOperator): operator is TranslatableComparisonOperator +export function isComparisonOperatorNeedTranslate(operator?: ComparisonOperator): operator is TranslatableComparisonOperator { if (!operator) return false - return !notTranslateKey.includes(operator) + return !(notTranslateKey as readonly ComparisonOperator[]).includes(operator) } export const getOperators = (type?: MetadataFilteringVariableType) => { diff --git a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx index 8606a0b307..e54bb6ee10 100644 --- a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx @@ -66,13 +66,13 @@ const FilterCondition: FC = ({ if (isSelect) { if (condition.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) { return FILE_TYPE_OPTIONS.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string, + name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }), value: item.value, })) } if (condition.key === 'transfer_method') { return TRANSFER_METHOD.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string, + name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }), value: item.value, })) } diff --git a/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx b/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx index dc760d7467..118e3a7c0a 100644 --- a/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx +++ b/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx @@ -121,7 +121,7 @@ const ConfigPromptItem: FC = ({ {t(`${i18nPrefix}.roleDescription.${payload.role}` as any, { ns: 'workflow' }) as string} +
{payload.role && t(`${i18nPrefix}.roleDescription.${payload.role}`, { ns: 'workflow' })}
} triggerClassName="w-4 h-4" /> diff --git a/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx b/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx index 7a1354176f..63269a85bf 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx @@ -34,7 +34,7 @@ const ConditionValue = ({ const variableSelector = variable_selector as ValueSelector - const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`nodes.ifElse.comparisonOperator.${operator}` as any, { ns: 'workflow' }) as string : operator + const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`nodes.ifElse.comparisonOperator.${operator}`, { ns: 'workflow' }) : operator const formatValue = useCallback((c: Condition) => { const notHasValue = comparisonOperatorNotRequireValue(c.comparison_operator) if (notHasValue) @@ -59,7 +59,7 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(c.value) ? c.value[0] : c.value))[0] return name - ? (t(`nodes.ifElse.optionName.${name.i18nKey}` as any, { ns: 'workflow' }) as string).replace(/\{\{#([^#]*)#\}\}/g, (a: string, b: string) => { + ? t(`nodes.ifElse.optionName.${name.i18nKey}`, { ns: 'workflow' }).replace(/\{\{#([^#]*)#\}\}/g, (a: string, b: string) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` @@ -91,9 +91,9 @@ const ConditionValue = ({ sub_variable_condition?.conditions.map((c: Condition, index) => (
{c.key}
-
{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`nodes.ifElse.comparisonOperator.${c.comparison_operator}` as any, { ns: 'workflow' }) as string : c.comparison_operator}
+
{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`nodes.ifElse.comparisonOperator.${c.comparison_operator}`, { ns: 'workflow' }) : c.comparison_operator}
{c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) &&
{isSelect(c) ? selectName(c) : formatValue(c)}
} - {index !== sub_variable_condition.conditions.length - 1 && (
{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}` as any, { ns: 'workflow' }) as string}
)} + {index !== sub_variable_condition.conditions.length - 1 && (
{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}`, { ns: 'workflow' })}
)}
)) } diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx index 840a02b7ea..56d046b05f 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx @@ -145,13 +145,13 @@ const ConditionItem = ({ if (isSelect) { if (fileAttr?.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) { return FILE_TYPE_OPTIONS.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string, + name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }), value: item.value, })) } if (fileAttr?.key === 'transfer_method') { return TRANSFER_METHOD.map(item => ({ - name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string, + name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }), value: item.value, })) } diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx index 98f55cce5c..ecbdcb7355 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx @@ -39,7 +39,7 @@ const ConditionOperator = ({ const options = useMemo(() => { return getOperators(varType, file).map((o) => { return { - label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}` as any, { ns: 'workflow' }) as string : o, + label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`, { ns: 'workflow' }) : o, value: o, } }) @@ -65,7 +65,7 @@ const ConditionOperator = ({ { selectedOption ? selectedOption.label - : t(`${i18nPrefix}.select` as any, { ns: 'workflow' }) as string + : t(`${i18nPrefix}.select`, { ns: 'workflow' }) } diff --git a/web/app/components/workflow/nodes/loop/components/condition-value.tsx b/web/app/components/workflow/nodes/loop/components/condition-value.tsx index 5532481dde..f1aeaa29d2 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-value.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-value.tsx @@ -27,7 +27,7 @@ const ConditionValue = ({ value, }: ConditionValueProps) => { const { t } = useTranslation() - const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`nodes.ifElse.comparisonOperator.${operator}` as any, { ns: 'workflow' }) as string : operator + const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`nodes.ifElse.comparisonOperator.${operator}`, { ns: 'workflow' }) : operator const notHasValue = comparisonOperatorNotRequireValue(operator) const formatValue = useMemo(() => { if (notHasValue) @@ -50,7 +50,7 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(value) ? value[0] : value))[0] return name - ? (t(`nodes.ifElse.optionName.${name.i18nKey}` as any, { ns: 'workflow' }) as string).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + ? t(`nodes.ifElse.optionName.${name.i18nKey}`, { ns: 'workflow' }).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` diff --git a/web/app/components/workflow/nodes/loop/default.ts b/web/app/components/workflow/nodes/loop/default.ts index 559a907593..943904b10e 100644 --- a/web/app/components/workflow/nodes/loop/default.ts +++ b/web/app/components/workflow/nodes/loop/default.ts @@ -1,5 +1,6 @@ import type { NodeDefault } from '../../types' import type { LoopNodeType } from './types' +import type { I18nKeysByPrefix } from '@/types/i18n' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' import { BlockEnum } from '@/app/components/workflow/types' import { genNodeMetaData } from '@/app/components/workflow/utils' @@ -79,17 +80,22 @@ const nodeDefault: NodeDefault = { }, } +type OptionItem = { + value: string + i18nKey: I18nKeysByPrefix<'workflow', 'nodes.ifElse.optionName.'> +} + export const FILE_TYPE_OPTIONS = [ { value: 'image', i18nKey: 'image' }, { value: 'document', i18nKey: 'doc' }, { value: 'audio', i18nKey: 'audio' }, { value: 'video', i18nKey: 'video' }, -] +] as const satisfies readonly OptionItem[] export const TRANSFER_METHOD = [ { value: TransferMethod.local_file, i18nKey: 'localUpload' }, { value: TransferMethod.remote_url, i18nKey: 'url' }, -] +] as const satisfies readonly OptionItem[] export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method', 'related_id'] export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method') diff --git a/web/app/components/workflow/nodes/loop/utils.ts b/web/app/components/workflow/nodes/loop/utils.ts index dba5460824..8ec9cd241f 100644 --- a/web/app/components/workflow/nodes/loop/utils.ts +++ b/web/app/components/workflow/nodes/loop/utils.ts @@ -13,12 +13,17 @@ const notTranslateKey = [ ComparisonOperator.largerThanOrEqual, ComparisonOperator.lessThan, ComparisonOperator.lessThanOrEqual, -] +] as const -export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => { +type NotTranslateOperator = typeof notTranslateKey[number] +export type TranslatableComparisonOperator = Exclude + +export function isComparisonOperatorNeedTranslate(operator: ComparisonOperator): operator is TranslatableComparisonOperator +export function isComparisonOperatorNeedTranslate(operator?: ComparisonOperator): operator is TranslatableComparisonOperator +export function isComparisonOperatorNeedTranslate(operator?: ComparisonOperator): operator is TranslatableComparisonOperator { if (!operator) return false - return !notTranslateKey.includes(operator) + return !(notTranslateKey as readonly ComparisonOperator[]).includes(operator) } export const getOperators = (type?: VarType, file?: { key: string }) => {