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.