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

View File

@@ -137,6 +137,7 @@
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import SearchSelect from '~/components/common/SearchSelect.vue'; import SearchSelect from '~/components/common/SearchSelect.vue';
import { useApi } from '~/composables/useApi'; import { useApi } from '~/composables/useApi';
import { extractCollection } from '~/shared/utils/apiHelpers';
import type { import type {
ComponentModelPiece, ComponentModelPiece,
ComponentModelProduct, ComponentModelProduct,
@@ -243,22 +244,6 @@ const pieceLoadingByPath = ref<Record<string, boolean>>({});
const productLoadingByPath = ref<Record<string, boolean>>({}); const productLoadingByPath = ref<Record<string, boolean>>({});
const componentLoadingByPath = 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) => { const setLoading = (target: Record<string, boolean>, key: string, value: boolean) => {
target[key] = value; target[key] = value;
}; };
@@ -362,7 +347,7 @@ const fetchPieceOptions = async (assignment: StructurePieceAssignment, term = ''
const definition = assignment.definition || {}; const definition = assignment.definition || {};
const requiredTypeId = const requiredTypeId =
definition.typePieceId || (definition as any).typePiece?.id || null; definition.typePieceId || definition.typePiece?.id || null;
const params = new URLSearchParams(); const params = new URLSearchParams();
params.set('itemsPerPage', '50'); params.set('itemsPerPage', '50');
@@ -392,7 +377,7 @@ const fetchProductOptions = async (assignment: StructureProductAssignment, term
const definition = assignment.definition || {}; const definition = assignment.definition || {};
const requiredTypeId = const requiredTypeId =
definition.typeProductId || (definition as any).typeProduct?.id || null; definition.typeProductId || definition.typeProduct?.id || null;
const params = new URLSearchParams(); const params = new URLSearchParams();
params.set('itemsPerPage', '50'); params.set('itemsPerPage', '50');
@@ -447,14 +432,14 @@ const describePieceRequirement = (assignment: StructurePieceAssignment) => {
addPart(definition.role); addPart(definition.role);
const explicitLabel = const explicitLabel =
definition.typePieceLabel || definition.typePieceLabel ||
(definition as any).typePiece?.name || definition.typePiece?.name ||
(definition.typePieceId ? props.pieceTypeLabelMap[definition.typePieceId] : null) || (definition.typePieceId ? props.pieceTypeLabelMap[definition.typePieceId] : null) ||
fallbackType?.name; fallbackType?.name;
addPart(explicitLabel); addPart(explicitLabel);
const family = const family =
definition.familyCode || definition.familyCode ||
(definition as any).typePiece?.code || definition.typePiece?.code ||
fallbackType?.code || fallbackType?.code ||
null; null;
if (family) { if (family) {
@@ -483,7 +468,7 @@ const getProductOptions = (assignment: StructureProductAssignment) => {
const definition = assignment.definition; const definition = assignment.definition;
const requiredTypeId = const requiredTypeId =
definition.typeProductId || definition.typeProductId ||
(definition as any).typeProduct?.id || definition.typeProduct?.id ||
definition.familyCode || definition.familyCode ||
null; null;
@@ -494,7 +479,7 @@ const getProductOptions = (assignment: StructureProductAssignment) => {
if (!requiredTypeId) { if (!requiredTypeId) {
return true; return true;
} }
if (definition.typeProductId || (definition as any).typeProduct?.id) { if (definition.typeProductId || definition.typeProduct?.id) {
return ( return (
product.typeProductId === requiredTypeId || product.typeProductId === requiredTypeId ||
product.typeProduct?.id === requiredTypeId product.typeProduct?.id === requiredTypeId
@@ -550,14 +535,14 @@ const describeProductRequirement = (assignment: StructureProductAssignment) => {
addPart(definition.role); addPart(definition.role);
const explicitLabel = const explicitLabel =
definition.typeProductLabel || definition.typeProductLabel ||
(definition as any).typeProduct?.name || definition.typeProduct?.name ||
(definition.typeProductId ? props.productTypeLabelMap[definition.typeProductId] : null) || (definition.typeProductId ? props.productTypeLabelMap[definition.typeProductId] : null) ||
fallbackType?.name; fallbackType?.name;
addPart(explicitLabel); addPart(explicitLabel);
const family = const family =
definition.familyCode || definition.familyCode ||
(definition as any).typeProduct?.code || definition.typeProduct?.code ||
fallbackType?.code || fallbackType?.code ||
null; null;
if (family) { if (family) {
@@ -617,7 +602,7 @@ const getPieceOptions = (assignment: StructurePieceAssignment) => {
const definition = assignment.definition; const definition = assignment.definition;
const requiredTypeId = const requiredTypeId =
definition.typePieceId || definition.typePieceId ||
(definition as any).typePiece?.id || definition.typePiece?.id ||
definition.familyCode || definition.familyCode ||
null; null;
@@ -628,7 +613,7 @@ const getPieceOptions = (assignment: StructurePieceAssignment) => {
if (!requiredTypeId) { if (!requiredTypeId) {
return true; return true;
} }
if (definition.typePieceId || (definition as any).typePiece?.id) { if (definition.typePieceId || definition.typePiece?.id) {
return ( return (
piece.typePieceId === requiredTypeId || piece.typePieceId === requiredTypeId ||
piece.typePiece?.id === 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 ModelTypesToolbar from "~/components/model-types/Toolbar.vue";
import ModelTypesTable from "~/components/model-types/Table.vue"; import ModelTypesTable from "~/components/model-types/Table.vue";
import { useApi } from "~/composables/useApi"; import { useApi } from "~/composables/useApi";
import { extractCollection } from "~/shared/utils/apiHelpers";
import { import {
deleteModelType, deleteModelType,
listModelTypes, listModelTypes,
@@ -153,7 +154,7 @@ useHead(() => ({
const extractErrorMessage = (error: unknown) => { const extractErrorMessage = (error: unknown) => {
if (error && typeof error === "object") { if (error && typeof error === "object") {
const maybeFetchError = error as { const maybeFetchError = error as {
data?: any; data?: Record<string, unknown>;
statusMessage?: string; statusMessage?: string;
message?: string; message?: string;
}; };
@@ -208,8 +209,8 @@ const refresh = async ({
total.value = response.total; total.value = response.total;
offset.value = response.offset; offset.value = response.offset;
limit.value = response.limit; limit.value = response.limit;
} catch (error: any) { } catch (error: unknown) {
if (error?.name === "AbortError") { if (error && typeof error === "object" && (error as { name?: string }).name === "AbortError") {
return; return;
} }
showError(extractErrorMessage(error)); showError(extractErrorMessage(error));
@@ -292,10 +293,12 @@ const openEditPage = (item: ModelType) => {
}); });
}; };
const { confirm } = useConfirm()
const confirmDelete = async (item: ModelType) => { const confirmDelete = async (item: ModelType) => {
const confirmed = window.confirm( const confirmed = await confirm({
"Supprimer ce type ? Cette action est irréversible." message: 'Supprimer ce type ? Cette action est irréversible.',
); });
if (!confirmed) { if (!confirmed) {
return; return;
} }
@@ -363,22 +366,6 @@ const relatedModalSubtitle = computed(() => {
return `${count} ${labels.plural} liés.`; 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 buildModelTypeIri = (id: string) => `/api/model_types/${id}`;
const resolveRelatedConfig = (category: ModelCategory) => { const resolveRelatedConfig = (category: ModelCategory) => {
@@ -401,22 +388,26 @@ const resolveRelatedEditBasePath = (category: ModelCategory) => {
return "/product"; return "/product";
}; };
const mapRelatedEntry = (item: any): RelatedEntry | null => { const mapRelatedEntry = (item: unknown): RelatedEntry | null => {
if (!item || typeof item !== "object" || typeof item.id !== "string") { if (!item || typeof item !== "object") {
return null;
}
const record = item as Record<string, unknown>;
if (typeof record.id !== "string") {
return null; return null;
} }
const name = const name =
typeof item.name === "string" && item.name.trim() typeof record.name === "string" && record.name.trim()
? item.name ? record.name
: "Sans nom"; : "Sans nom";
const reference = const reference =
typeof item.reference === "string" && item.reference.trim() typeof record.reference === "string" && record.reference.trim()
? item.reference ? record.reference
: typeof item.code === "string" && item.code.trim() : typeof record.code === "string" && record.code.trim()
? item.code ? record.code
: null; : null;
return { return {
id: item.id, id: record.id,
name, name,
reference, reference,
}; };

View File

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

View File

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