I got this code, which is supposed to sort items
based on their first character into groups.
So assuming I got an array of item
like this:
- {name: 'Foo'}
- {name: 'Bar'}
- {name: 'Baz'}
The result should be like:
B: - Bar
- Baz F: - Foo
This is my code to get this working - note that I am using a computed property which is just a convenient VueJS way to handle change on an item without having to manually watch this change:
<script lang="ts">
const computedItems = computed(() => {
if (!items.value) return {};
const grouped = items.value.data.reduce((r, item) => {
let group = item.name[0];
if (!r[group]) r[group] = {group, children: [item]}
else r[group].children.push(item);
return r;
}, {});
return Object.keys(grouped).sort().reduce(
(obj, key) => {
obj[key] = grouped[key];
return obj;
},
{} as Record<string, { group: string, children: Item[] }>
);
});
</script>
Now I get thes warning:
TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. No index signature with a parameter of type 'string' was found on type '{}'.
This is shown for:
if (!r[group]) r[group]...
else r[group]...
and
obj[key] = grouped[key];
The data itself is coming from this api-call:
type ItemResult = { data: ItemData[], links?: any, meta?: any };
const {
data: items,
} = await useLazyAsyncApi<ItemResult>(`/api/item`);
The ItemData
is just a simple type:
export type ItemData = {
uuid: string;
company: App.Domain.Company.Data.CompanyData;
};
export type CompanyData = {
uuid: string;
name: string;
url: string | null;
phone: string | null;
email: string | null;
created_at: string;
deleted_at: string | null;
};
My question is: Why can't an expression of type 'string' be used as index here and how can I avoid this error?
One thing: The code actually works. It's "just" ts complaining.
CodePudding user response:
I assume you're missing a type on your first reduce()
call, as Typescript cannot know that your {}
reduce initializer is of type Record<string, { group: string, children: Item[] }>
:
const grouped = items.value.data.reduce((r, item) => {
let group = item.name[0];
r[group] = r[group] || {group, children: []}
r[group].children.push(item);
return r;
}, {} as Record<string, { group: string, children: Item[] }>); // <- HERE
return Object.keys(grouped).sort().reduce(
(obj, key) => {
obj[key] = grouped[key];
return obj;
},
{} as Record<string, { group: string, children: Item[] }>
);
Aside from your question, you can sort()
your data first, which will avoid a second pass on grouped
result :
const grouped = items.value.data
.sort((i1, i2) => i1.name.localeCompare(i2.name))
.reduce((r, item) => {
let group = item.name[0];
r[group] = r[group] || {group, children: []}
r[group].children.push(item);
return r;
}, {} as Record<string, { group: string, children: Item[] }>);