Ethan Richardson
Ethan Richardson

Reputation: 491

Can not add an array item to an existing array at the start of the array

I am trying to add a new array item to the start of an existing array and get an error. I am trying to add a new array item to the array that is constructed on the fly. The example code below causes an error using the unshift operation.

The array that I want to insert to is lRtn.document.mainTemplate.item.items and I am using lodash to iterate over the parameter _pUrls and for each url I am creating a new array item and want to push the new array item to the existing array lRtn.document.mainTemplate.item.items leaving the existing array content in place.

The error is get is

Argument of type '{ type: string; description: string; source: any; }' is not assignable to parameter of type '{ type: string; duration: number; contentType?: undefined; content?: undefined; } | { type: string; contentType: string; content: string; duration?: undefined; }'.
  Type '{ type: string; description: string; source: any; }' is missing the following properties from type '{ type: string; contentType: string; content: string; duration?: undefined; }': contentType, content

The code that I have tried is here below, any help or guidance would be much appreciated.

export async function createAPLADirectiveBookReviews(
  _pUrls: any,
  _pFollowOn: string,
) {

  const lDescription = 'review';

  const lRtn = {
    type: constants.APLA.directiveType,
    token: constants.APLA.token,
    document: {
      version: '0.91',
      type: 'APLA',
      description: 'This document demonstrates key components used to create audio responses.',
      mainTemplate: {
        parameters: ['payload'],
        item: {
          type: 'Sequencer',
          description: 'Play this audio back in sequence',
          items: [
            {
              type: 'Silence',
              duration: 1500,
            },
            {
              type: 'Speech',
              contentType: 'PlainText',
              content: _pFollowOn,
            },
          ],
        },
      },
    },
    datasources: {
    },
  };

  // iterate over the urls and add them to the list
  _.forEach(_pUrls, function (url) {

    let lArrayItem = { 'type': 'Audio', 'description': lDescription, 'source': url };

    lRtn.document.mainTemplate.item.items.unshift(lArrayItem);

    });

  return lRtn;
}

Playground link

Upvotes: 1

Views: 41

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074148

TypeScript infers the type of the lRtn.document.mainTemplate.item.items array as an array of this union type (except I've put the properties in the same order in both of them; TypeScript doesn't care about property order):

{
    type: string;
    duration: number;
    contentType?: undefined;
    content?: undefined;
} | {
    type: string;
    duration?: undefined;
    contentType: string;
    content: string;
}

That means all members must match one of those two object shapes. But the object you're trying to put in the array doesn't match, it has this shape:

{
    type: string;
    description: string;
    source: any;
}

That doesn't have duration (which it would need to match the first type of the union) and also doesn't have contentType or content (which it would need for the second type of the union). So you can't put it in that array.

You need to either give an explicit type the items array that makes those properties options, or make your lArrayItem object fit the shape of one of those union types, so it fits into the array.

The best way to explicitly give a type to that items array would be to define a type for lRtn:

interface ItemType {
    type: string;
    duration?: number;
    contentType?: string;
    content?: string;
}
interface ReturnType {
    document: {
        version: string;
        type: string;
        description: string;
        mainTemplate: {
            parameters: string[];
            item: {
                type: string;
                description: string;
                items: ItemType[];
            };
        };
    },
    dataSources: {
        // ...
    }
};

(probably in smaller parts than that) and use const lRtn: ReturnType = ... to assign a type to the whole thing.

But you can also do it with an as expression on the array:

interface ItemType {
    type: string;
    duration?: number;
    contentType?: string;
    content?: string;
}
// ...
const lRtn = {
          // ...
          items: [
              {
                  type: 'Silence',
                  duration: 1500,
              },
              {
                  type: 'Speech',
                  contentType: 'PlainText',
                  content: _pFollowOn,
              },
          ] as ItemType[], // <<============
          // ...
};

Upvotes: 1

Related Questions