refactor(types): eliminate explicit any casts across components (F3.3)

Extend ComponentModelPiece/Product with optional typePiece/typeProduct
nested objects. Replace 12 'as any' casts in assignment node, convert
Promise<any> to Promise<unknown>, use Record<string, unknown> at API
boundaries. ~15 casts eliminated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-02-09 11:13:50 +01:00
parent a6664ce9a2
commit efe1fd2a73
5 changed files with 46 additions and 66 deletions

View File

@@ -115,7 +115,7 @@ const applyCustomFieldOptions = (node: Record<string, any> | null | undefined) =
}
if (Array.isArray(node.customFields)) {
node.customFields = node.customFields.map((field: any) => {
node.customFields = node.customFields.map((field: Record<string, any>) => {
if (!field || typeof field !== 'object') {
return field
}
@@ -145,7 +145,7 @@ const applyCustomFieldOptions = (node: Record<string, any> | null | undefined) =
}
if (Array.isArray(node.subcomponents)) {
node.subcomponents = node.subcomponents.map((sub: any) => {
node.subcomponents = node.subcomponents.map((sub: Record<string, any>) => {
if (!sub || typeof sub !== 'object') {
return sub
}
@@ -246,7 +246,7 @@ watch(
)
onMounted(async () => {
const loaders: Promise<any>[] = []
const loaders: Promise<unknown>[] = []
if (!availablePieceTypes.value.length) {
loaders.push(loadPieceTypes())
}

View File

@@ -137,6 +137,7 @@
import { computed, ref, watch } from 'vue';
import SearchSelect from '~/components/common/SearchSelect.vue';
import { useApi } from '~/composables/useApi';
import { extractCollection } from '~/shared/utils/apiHelpers';
import type {
ComponentModelPiece,
ComponentModelProduct,
@@ -243,22 +244,6 @@ const pieceLoadingByPath = ref<Record<string, boolean>>({});
const productLoadingByPath = ref<Record<string, boolean>>({});
const componentLoadingByPath = ref<Record<string, boolean>>({});
const extractCollection = (payload: any): any[] => {
if (Array.isArray(payload)) {
return payload;
}
if (Array.isArray(payload?.member)) {
return payload.member;
}
if (Array.isArray(payload?.['hydra:member'])) {
return payload['hydra:member'];
}
if (Array.isArray(payload?.data)) {
return payload.data;
}
return [];
};
const setLoading = (target: Record<string, boolean>, key: string, value: boolean) => {
target[key] = value;
};
@@ -362,7 +347,7 @@ const fetchPieceOptions = async (assignment: StructurePieceAssignment, term = ''
const definition = assignment.definition || {};
const requiredTypeId =
definition.typePieceId || (definition as any).typePiece?.id || null;
definition.typePieceId || definition.typePiece?.id || null;
const params = new URLSearchParams();
params.set('itemsPerPage', '50');
@@ -392,7 +377,7 @@ const fetchProductOptions = async (assignment: StructureProductAssignment, term
const definition = assignment.definition || {};
const requiredTypeId =
definition.typeProductId || (definition as any).typeProduct?.id || null;
definition.typeProductId || definition.typeProduct?.id || null;
const params = new URLSearchParams();
params.set('itemsPerPage', '50');
@@ -447,14 +432,14 @@ const describePieceRequirement = (assignment: StructurePieceAssignment) => {
addPart(definition.role);
const explicitLabel =
definition.typePieceLabel ||
(definition as any).typePiece?.name ||
definition.typePiece?.name ||
(definition.typePieceId ? props.pieceTypeLabelMap[definition.typePieceId] : null) ||
fallbackType?.name;
addPart(explicitLabel);
const family =
definition.familyCode ||
(definition as any).typePiece?.code ||
definition.typePiece?.code ||
fallbackType?.code ||
null;
if (family) {
@@ -483,7 +468,7 @@ const getProductOptions = (assignment: StructureProductAssignment) => {
const definition = assignment.definition;
const requiredTypeId =
definition.typeProductId ||
(definition as any).typeProduct?.id ||
definition.typeProduct?.id ||
definition.familyCode ||
null;
@@ -494,7 +479,7 @@ const getProductOptions = (assignment: StructureProductAssignment) => {
if (!requiredTypeId) {
return true;
}
if (definition.typeProductId || (definition as any).typeProduct?.id) {
if (definition.typeProductId || definition.typeProduct?.id) {
return (
product.typeProductId === requiredTypeId ||
product.typeProduct?.id === requiredTypeId
@@ -550,14 +535,14 @@ const describeProductRequirement = (assignment: StructureProductAssignment) => {
addPart(definition.role);
const explicitLabel =
definition.typeProductLabel ||
(definition as any).typeProduct?.name ||
definition.typeProduct?.name ||
(definition.typeProductId ? props.productTypeLabelMap[definition.typeProductId] : null) ||
fallbackType?.name;
addPart(explicitLabel);
const family =
definition.familyCode ||
(definition as any).typeProduct?.code ||
definition.typeProduct?.code ||
fallbackType?.code ||
null;
if (family) {
@@ -617,7 +602,7 @@ const getPieceOptions = (assignment: StructurePieceAssignment) => {
const definition = assignment.definition;
const requiredTypeId =
definition.typePieceId ||
(definition as any).typePiece?.id ||
definition.typePiece?.id ||
definition.familyCode ||
null;
@@ -628,7 +613,7 @@ const getPieceOptions = (assignment: StructurePieceAssignment) => {
if (!requiredTypeId) {
return true;
}
if (definition.typePieceId || (definition as any).typePiece?.id) {
if (definition.typePieceId || definition.typePiece?.id) {
return (
piece.typePieceId === requiredTypeId ||
piece.typePiece?.id === requiredTypeId

View File

@@ -97,6 +97,7 @@ import { useHead, useRouter } from "#imports";
import ModelTypesToolbar from "~/components/model-types/Toolbar.vue";
import ModelTypesTable from "~/components/model-types/Table.vue";
import { useApi } from "~/composables/useApi";
import { extractCollection } from "~/shared/utils/apiHelpers";
import {
deleteModelType,
listModelTypes,
@@ -153,7 +154,7 @@ useHead(() => ({
const extractErrorMessage = (error: unknown) => {
if (error && typeof error === "object") {
const maybeFetchError = error as {
data?: any;
data?: Record<string, unknown>;
statusMessage?: string;
message?: string;
};
@@ -208,8 +209,8 @@ const refresh = async ({
total.value = response.total;
offset.value = response.offset;
limit.value = response.limit;
} catch (error: any) {
if (error?.name === "AbortError") {
} catch (error: unknown) {
if (error && typeof error === "object" && (error as { name?: string }).name === "AbortError") {
return;
}
showError(extractErrorMessage(error));
@@ -292,10 +293,12 @@ const openEditPage = (item: ModelType) => {
});
};
const { confirm } = useConfirm()
const confirmDelete = async (item: ModelType) => {
const confirmed = window.confirm(
"Supprimer ce type ? Cette action est irréversible."
);
const confirmed = await confirm({
message: 'Supprimer ce type ? Cette action est irréversible.',
});
if (!confirmed) {
return;
}
@@ -363,22 +366,6 @@ const relatedModalSubtitle = computed(() => {
return `${count} ${labels.plural} liés.`;
});
const extractCollection = (payload: any): any[] => {
if (Array.isArray(payload)) {
return payload;
}
if (Array.isArray(payload?.member)) {
return payload.member;
}
if (Array.isArray(payload?.["hydra:member"])) {
return payload["hydra:member"];
}
if (Array.isArray(payload?.items)) {
return payload.items;
}
return [];
};
const buildModelTypeIri = (id: string) => `/api/model_types/${id}`;
const resolveRelatedConfig = (category: ModelCategory) => {
@@ -401,22 +388,26 @@ const resolveRelatedEditBasePath = (category: ModelCategory) => {
return "/product";
};
const mapRelatedEntry = (item: any): RelatedEntry | null => {
if (!item || typeof item !== "object" || typeof item.id !== "string") {
const mapRelatedEntry = (item: unknown): RelatedEntry | null => {
if (!item || typeof item !== "object") {
return null;
}
const record = item as Record<string, unknown>;
if (typeof record.id !== "string") {
return null;
}
const name =
typeof item.name === "string" && item.name.trim()
? item.name
typeof record.name === "string" && record.name.trim()
? record.name
: "Sans nom";
const reference =
typeof item.reference === "string" && item.reference.trim()
? item.reference
: typeof item.code === "string" && item.code.trim()
? item.code
typeof record.reference === "string" && record.reference.trim()
? record.reference
: typeof record.code === "string" && record.code.trim()
? record.code
: null;
return {
id: item.id,
id: record.id,
name,
reference,
};

View File

@@ -278,10 +278,11 @@ const resetForm = () => {
? incoming.code
: generateCodeFromName(form.name)
form.category = incoming.category ?? props.initialCategory
const incomingRecord = incoming as Record<string, unknown>
form.notes = typeof incoming.notes === 'string'
? incoming.notes
: typeof (incoming as any).description === 'string'
? (incoming as any).description
: typeof incomingRecord.description === 'string'
? incomingRecord.description
: ''
errors.name = undefined

View File

@@ -17,6 +17,7 @@ export interface ComponentModelCustomField {
export interface ComponentModelPiece {
typePieceId?: string
typePieceLabel?: string
typePiece?: { id?: string; name?: string; code?: string } | null
reference?: string
familyCode?: string
role?: string
@@ -25,6 +26,7 @@ export interface ComponentModelPiece {
export interface ComponentModelProduct {
typeProductId?: string
typeProductLabel?: string
typeProduct?: { id?: string; name?: string; code?: string } | null
reference?: string
familyCode?: string
role?: string
@@ -33,6 +35,7 @@ export interface ComponentModelProduct {
export interface ComponentModelStructureNode {
typeComposantId?: string
typeComposantLabel?: string
typeComposant?: { id?: string; name?: string }
modelId?: string
familyCode?: string
alias?: string
@@ -151,8 +154,8 @@ const validatePiece = (
const typePieceId = ensureString(value.typePieceId)
const typePieceLabel = ensureString(value.typePieceLabel)
const reference = ensureString(value.reference)
const familyCode = ensureString((value as any).familyCode)
const role = ensureString((value as any).role)
const familyCode = ensureString(value.familyCode)
const role = ensureString(value.role)
if (!typePieceId && !typePieceLabel && !reference && !familyCode) {
issues.push(`${path}: au moins un identifiant, une famille ou une référence de pièce est requis`)
@@ -184,8 +187,8 @@ const validateStructureNode = (
const familyCode = ensureString(value.familyCode)
const alias = ensureString(value.alias)
const rawSubcomponents = Array.isArray((value as any).subcomponents)
? (value as any).subcomponents
const rawSubcomponents = Array.isArray(value.subcomponents)
? value.subcomponents
: []
const subcomponents: ComponentModelStructureNode[] = []