5

I want to add an extra property to an array, but I get trouble with declaring it.

small part

const arr: string[] & {key: number} = ['123'];//ts error  Property 'key' is missing
arr.key = 10;

large part

I need to group arr by index then sort it by value

add an index to array property will be a convenience if using Object.values but not Object.entries. I know there are many approaches to do this, but I think it's much easier if I am using plain js

type Arr = {index: number, value: number};
const arr:Arr[] = []
arr.push({
    index: 10,
    value: 2
})
arr.push({
    index: 10,
    value: 1
})
arr.push({
    index: 10,
    value: 3
});
arr.push({
    index: 20,
    value: 100
});
arr.push({
    index: 20,
    value: 50
});
arr.reduce<Record<string, Arr[] & {index: number}>>((prev, curr) => {
    if(!prev[curr.index]){
        prev[curr.index] = [];//ts error Property missing
        prev[curr.index].index = curr.index;
    }
    prev[curr.index].push(curr);
    prev[curr.index].sort((a,b) => a.value - b.value);//or some insertion sort algorithm.
    return prev;
},{})
hem
  • 1,012
  • 6
  • 11
dabuside
  • 103
  • 1
  • 8

2 Answers2

9

An array literal can't by itself satisfy the intersection, the simplest way you could do it is to use Object.assign which returns an intersection of its arguments:

const arr: string[] & {key: number} = Object.assign(['123'], {
    key: 10
});

And in your bigger example, the same could be used:

arr.reduce<Record<string, Arr[] & {index: number}>>((prev, curr) => {
    if(!prev[curr.index]){
        prev[curr.index] = Object.assign([], {
          index: curr.index
        })
    }
    prev[curr.index].push(curr);
    prev[curr.index].sort((a,b) => a.value - b.value);//or some insertion sort algorithm.
    return prev;
},{})

playground link

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • it works! TS lacks some conscience; we need to use some tricks to ensure type-safe while plain js can do directly. – dabuside Aug 05 '19 at 08:03
  • I'm slightly confused about how `Record<>` works here. Mind explaining it a bit? as far as I know, `reduce` has `T1` whose type is the accumulator type, while `T2` is the type of the original reduce array items, how is `Record` working here? – briosheje Aug 05 '19 at 08:13
  • @briosheje Here `Record` is used as a dictionary type from `string` to `Arr[] & {index: number}`. `reduce` only takes one type parameter, the type of the accumulator. – Titian Cernicova-Dragomir Aug 05 '19 at 08:18
  • @TitianCernicova-Dragomir Oh, thanks. I wasn't aware of reduce taking only one type argument. I personally wasn't even aware of `Record`, thanks for sharing! – briosheje Aug 05 '19 at 08:59
-1

In your small part example, the type declaration string[] & {key: number} can never be satisfied because an array of strings cannot be the same as an object with key property.

Concerning your large part example, what exactly are you trying to achieve? If you say you want to group an array by an index, and then sort it by value, do you mean that the index is kind of your primary sort criterion and value the secondary criterion? In that case, I'd suppose you write your own sorting function that takes both values into account. You'll find an example in this Stackoverflow thread.

SparkFountain
  • 2,110
  • 15
  • 35