Home > Enterprise >  Identify duplicate value in array of objects with a type
Identify duplicate value in array of objects with a type

Time:09-30

I have some typescript mapping arrays in my Angular app. See example below.

Is there is a way to have an error (build / type) when someone adds an object which has a path that already exists?

I've seen examples with flat arrays, but not with a specific property (path) in an array of objects.

I would also add that in my specific case (slightly different from my example) I don't want to merge the objects.

If needed I could create the mappings via a function as long as the error is not runtime.

interface MappedPath {
   path: string; // I always want this to be unique.
   personal: { name?: string; email?: string; } // ignore this can be same or different.
}

// should error as duplicate path values used.
const array: MappedPath[] = [
   {
     path: '/same/path/here',
     personal: { name: 'Henry' }
   },
   {
     path: '/same/path/here',
     personal: { email: '[email protected]' }
   }
]

// no errors. unique 'path' but 'personal' is same.
const array: MappedPath[] = [
   {
     path: '/path/A',
     personal: { email: '[email protected]' }
   },
   {
     path: '/path/A/somewhere',
     personal: { email: '[email protected]' }
   }
]


CodePudding user response:

You can use a modified duplicate remover function like so :

const invalid = [
  { path: 'some/path', data: {} },
  { path: 'some/path', data: {} },
];

const valid = [
  { path: 'some/different/path', data: {} },
  { path: 'some/path', data: {} },
];


checkUnicity(valid);
checkUnicity(invalid);

function checkUnicity(items) {
  if (
    items.some((v, i, a) => a
      .indexOf(
        items.find((item) => item.path === v.path)) !== i
      )
  ) throw new Error('Duplicated path');
}

CodePudding user response:

There currently is no stand-alone type which can represent such relations in TypeScript. Instead, we can use a generic helper function to initialize the array.

type ValidateMappedPath<
  T extends MappedPath[], 
  P extends string = never,
  O = T
> = T extends [infer L extends MappedPath, ...infer R extends MappedPath[]]
      ? L["path"] extends P
        ? never
        : ValidateMappedPath<R, P | L["path"], O>
      : O

function createMappedPath<
  T extends (MappedPath & { path: P })[], 
//                          ^^^^^^^ this makes sure that path is narrowed to 
//                                  be a string literal type                  
  P extends string
>(mappedPath: ValidateMappedPath<[...T]>){
//                               ^^^^^^ infer T to be a tuple and not an array
    return mappedPath
}

The generic function takes the array as the parameter and infers its type into T. The recursive ValidateMappedPath iterates through the array and checks for duplicate path values. If it finds any, the mappedPath parameter is typed as never leading to a compile time error.


Playground

  • Related