Fix relation field unknown target object (#13129)

Fixes https://github.com/twentyhq/twenty/issues/12867

Issue:
when you have a variable `toto` which is: `Record<string, MyType>` and
you do toto['xxx'], this will be typed as `MyType` instead of `MyType |
undefined`

Solutions:
- activate `noUncheckedIndexedAccess` check in tsconfig, this is the
preferred solution but will take time to get there (this raises 600+
errors)
- use a Map: cf https://github.com/twentyhq/twenty/pull/13125/files
- set the type to Partial<Record<string, MyType>>. Drawback is that when
you do Object.values(toto), you'll get `Array<MyType | undefined>`.
Hence why we have to filter these behind


<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/d0a0bfed-c441-4e53-84c2-2da98ccbcf50"
/>
This commit is contained in:
Charles Bochet
2025-07-09 15:43:11 +02:00
committed by GitHub
parent 156cb1b52f
commit 867619247f
23 changed files with 170 additions and 71 deletions

View File

@ -1,3 +1,5 @@
import { isDefined } from 'twenty-shared/utils';
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
@ -5,7 +7,9 @@ export const getObjectMetadataMapItemByNamePlural = (
objectMetadataMaps: ObjectMetadataMaps,
namePlural: string,
): ObjectMetadataItemWithFieldMaps | undefined => {
const objectMetadataItems = Object.values(objectMetadataMaps.byId);
const objectMetadataItems = Object.values(objectMetadataMaps.byId).filter(
isDefined,
);
return objectMetadataItems.find(
(objectMetadata) => objectMetadata.namePlural === namePlural,

View File

@ -1,3 +1,5 @@
import { isNonEmptyString } from '@sniptt/guards';
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
@ -5,7 +7,11 @@ export const getObjectMetadataMapItemByNameSingular = (
objectMetadataMaps: ObjectMetadataMaps,
nameSingular: string,
): ObjectMetadataItemWithFieldMaps | undefined => {
return objectMetadataMaps.byId[
objectMetadataMaps.idByNameSingular[nameSingular]
];
const objectMetadataId = objectMetadataMaps.idByNameSingular[nameSingular];
if (!isNonEmptyString(objectMetadataId)) {
return undefined;
}
return objectMetadataMaps.byId[objectMetadataId];
};

View File

@ -1,4 +1,5 @@
import { t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import {
ObjectMetadataException,
@ -19,14 +20,16 @@ export const validatesNoOtherObjectWithSameNameExistsOrThrows = ({
existingObjectMetadataId,
objectMetadataMaps,
}: ValidateNoOtherObjectWithSameNameExistsOrThrowsParams) => {
const objectAlreadyExists = Object.values(objectMetadataMaps.byId).find(
(objectMetadata) =>
(objectMetadata.nameSingular === objectMetadataNameSingular ||
objectMetadata.namePlural === objectMetadataNamePlural ||
objectMetadata.nameSingular === objectMetadataNamePlural ||
objectMetadata.namePlural === objectMetadataNameSingular) &&
objectMetadata.id !== existingObjectMetadataId,
);
const objectAlreadyExists = Object.values(objectMetadataMaps.byId)
.filter(isDefined)
.find(
(objectMetadata) =>
(objectMetadata.nameSingular === objectMetadataNameSingular ||
objectMetadata.namePlural === objectMetadataNamePlural ||
objectMetadata.nameSingular === objectMetadataNamePlural ||
objectMetadata.namePlural === objectMetadataNameSingular) &&
objectMetadata.id !== existingObjectMetadataId,
);
if (objectAlreadyExists) {
throw new ObjectMetadataException(