fix(types): fix TypeScript errors in controls and filters

- Revert DatasourceControlProps to required props and add proper TestProps interface in tests
- Fix AdhocFilterOptionProps interface to match actual callback signatures
- Add optional chaining for onChange callbacks in AdhocFilterControl
- Fix AdhocFilter class type casting for translateToSql compatibility
- Add @ts-expect-error for propTypes/defaultProps assignments

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Evan Rusackas
2025-12-19 09:12:50 -08:00
parent 419e505d36
commit 6878882d9f
5 changed files with 77 additions and 29 deletions

View File

@@ -73,8 +73,39 @@ const mockDatasource: TestDatasource = {
sql: 'SELECT * FROM mock_datasource_sql',
};
interface TestProps {
hovered: boolean;
type: string;
label: string;
default: null;
description: null;
value: string;
form_data: JsonObject;
datasource: TestDatasource;
validationErrors: string[];
name: string;
actions: {
changeDatasource: jest.Mock;
setControlValue: jest.Mock;
};
isEditable: boolean;
user: {
createdOn: string;
email: string;
firstName: string;
isActive: boolean;
lastName: string;
permissions: JsonObject;
roles: JsonObject;
userId: number;
username: string;
};
onChange: jest.Mock;
onDatasourceSave: jest.Mock;
}
// Use type assertion for test props since the component is wrapped with withTheme
const createProps = (overrides: JsonObject = {}): Record<string, unknown> => ({
const createProps = (overrides: JsonObject = {}): TestProps => ({
hovered: false,
type: 'DatasourceControl',
label: 'Datasource',

View File

@@ -82,15 +82,15 @@ interface FormData {
}
interface DatasourceControlProps {
actions?: DatasourceControlActions;
actions: DatasourceControlActions;
onChange?: () => void;
value?: string | null;
datasource?: ExtendedDatasource;
datasource: ExtendedDatasource;
form_data?: FormData;
isEditable?: boolean;
onDatasourceSave?: ((datasource: ExtendedDatasource) => void) | null;
theme?: SupersetTheme;
user?: User;
theme: SupersetTheme;
user: User;
// ControlHeader-related props
hovered?: boolean;
type?: string;
@@ -99,8 +99,6 @@ interface DatasourceControlProps {
description?: string | null;
validationErrors?: string[];
name?: string;
// Allow additional props
[key: string]: unknown;
}
interface DatasourceControlState {

View File

@@ -77,7 +77,8 @@ export default class AdhocFilter {
this.sqlExpression =
typeof adhocFilter.sqlExpression === 'string'
? adhocFilter.sqlExpression
: translateToSql(adhocFilter, { useSimple: true });
// Cast to unknown first to handle type mismatch between AdhocFilterInput and AdhocFilter from @superset-ui/core
: translateToSql(adhocFilter as unknown as Parameters<typeof translateToSql>[0], { useSimple: true });
this.clause = adhocFilter.clause;
if (
adhocFilter.operator &&
@@ -106,12 +107,24 @@ export default class AdhocFilter {
}
duplicateWith(nextFields: Partial<AdhocFilterInput>): AdhocFilter {
return new AdhocFilter({
...this,
// all duplicated fields are not new (i.e. will not open popup automatically)
isNew: false,
// Spread class properties as plain object for constructor input
const currentFields: AdhocFilterInput = {
expressionType: this.expressionType,
subject: this.subject,
operator: this.operator,
operatorId: this.operatorId,
comparator: this.comparator,
clause: this.clause,
sqlExpression: this.sqlExpression,
isExtra: this.isExtra,
isNew: false, // all duplicated fields are not new
datasourceWarning: this.datasourceWarning,
deck_slices: this.deck_slices,
layerFilterScope: this.layerFilterScope,
filterOptionName: this.filterOptionName,
...nextFields,
});
};
return new AdhocFilter(currentFields);
}
equals(adhocFilter: AdhocFilter): boolean {
@@ -163,6 +176,7 @@ export default class AdhocFilter {
}
translateToSql(): string {
return translateToSql(this);
// Cast to unknown first to handle type mismatch between class and @superset-ui/core AdhocFilter type
return translateToSql(this as unknown as Parameters<typeof translateToSql>[0]);
}
}

View File

@@ -209,7 +209,7 @@ class AdhocFilterControl extends Component<AdhocFilterControlProps, AdhocFilterC
this.onRemoveFilter(index);
}}
onMoveLabel={this.moveLabel}
onDropLabel={() => this.props.onChange(this.state.values)}
onDropLabel={() => this.props.onChange?.(this.state.values)}
partitionColumn={this.state.partitionColumn}
/>
);
@@ -282,7 +282,7 @@ class AdhocFilterControl extends Component<AdhocFilterControlProps, AdhocFilterC
...prevState,
values: valuesCopy,
}));
this.props.onChange(valuesCopy);
this.props.onChange?.(valuesCopy);
}
onRemoveFilter(index: number): void {
@@ -305,14 +305,14 @@ class AdhocFilterControl extends Component<AdhocFilterControlProps, AdhocFilterC
values: [...prevState.values, mappedOption],
}),
() => {
this.props.onChange(this.state.values);
this.props.onChange?.(this.state.values);
},
);
}
}
onFilterEdit(changedFilter: AdhocFilter): void {
this.props.onChange(
this.props.onChange?.(
this.state.values.map(value => {
if (value.filterOptionName === changedFilter.filterOptionName) {
return changedFilter;
@@ -325,14 +325,15 @@ class AdhocFilterControl extends Component<AdhocFilterControlProps, AdhocFilterC
onChange(opts: FilterOption[] | null): void {
const options = (opts || [])
.map(option => this.mapOption(option))
.filter(option => option);
this.props.onChange(options);
.filter((option): option is AdhocFilter => option !== null);
this.props.onChange?.(options);
}
getMetricExpression(savedMetricName: string): string {
return this.props.savedMetrics.find(
const metric = this.props.savedMetrics?.find(
savedMetric => savedMetric.metric_name === savedMetricName,
).expression;
);
return metric?.expression ?? '';
}
moveLabel(dragIndex: number, hoverIndex: number): void {
@@ -429,7 +430,10 @@ class AdhocFilterControl extends Component<AdhocFilterControlProps, AdhocFilterC
}
}
// Static properties are defined in the class using static keyword
// @ts-expect-error - propTypes are defined for runtime validation but TypeScript handles type checking
AdhocFilterControl.propTypes = propTypes;
// @ts-expect-error - defaultProps for backward compatibility with PropTypes
AdhocFilterControl.defaultProps = defaultProps;
export default withTheme(AdhocFilterControl);

View File

@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import type React from 'react';
import { OptionControlLabel } from 'src/explore/components/controls/OptionControls';
import { DndItemType } from 'src/explore/components/DndItemType';
import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger';
@@ -26,14 +27,14 @@ import { useGetTimeRangeLabel } from '../utils';
export interface AdhocFilterOptionProps {
adhocFilter: AdhocFilter;
onFilterEdit: () => void;
onRemoveFilter: () => void;
onFilterEdit: (editedFilter: AdhocFilter) => void;
onRemoveFilter: (e: React.MouseEvent) => void;
options: OptionSortType[];
sections: string[];
operators: Operators[];
datasource: Record<string, any>;
partitionColumn: string;
onMoveLabel: () => void;
sections?: string[];
operators?: string[];
datasource?: Record<string, unknown>;
partitionColumn?: string | null;
onMoveLabel: (dragIndex: number, hoverIndex: number) => void;
onDropLabel: () => void;
index: number;
}