Home > database >  How can I use arrays with object properties in typescript
How can I use arrays with object properties in typescript

Time:11-03

Is there a way to use arrays with object properties without using @ts-ignore in typescript

Lets say I have this function, it attempts to convert an array to an array with the prop property. This works fine except for the fact that their is a @ts-ignore

    const addPropToArray = <T extends unknown>(x: T[]): T[] & {prop: number} => {
      //@ts-ignore
      const y: T[] & {prop: number} = [...x];
      y.prop = 3;
      return y
    };

Obviously I can't do this

const x: string[] & {prop:number} = // what could go here

by itself which is why I was thinking of using the function but even then the function must have a @ts-ignore.

Are arrays with object properties just such a bad idea that typescript doesn't try to support them well, or is their something I'm missing?

Maybe their is some variation of this that could work?

const x: string[] & {prop:number} = {...["a","b","c"], prop: 4}

obviously the problem here is that it isn't an array any more.

EDIT: I realize I could just cast the value instead of using @ts-ignore but that sill doesn't seem like the best solution

CodePudding user response:

Obviously I can't do this const x: string[] & {prop:number} = // what could go here

You could do this, if you assign the returned value of a self-executing anonymous function. Something like this, for example:

interface ArrayProps {
    prop: number;
}

const x: string[] & ArrayProps = (() => {
    const xPartial: string[] & Partial<ArrayProps> = [];
    xPartial.prop = 3;

    return xPartial as string[] & ArrayProps;
})();

TypeScript Playground

I'm not sure if there's a way to do this without a type assertion, though. Adding a custom type guard instead of the type assertion would require accounting for the path where the type guard fails, so x would end up also being able to be undefined or something depending on how that path is handled.

CodePudding user response:

The Object.assign(target, source) method copies the enumerable own properties from a source object onto the target object and returns the augmented target. And the TypeScript standard library typings for this method looks like:

interface ObjectConstructor {
    assign<T, U>(target: T, source: U): T & U;
}

Meaning that it models the operation as producing the intersection of the types of source and the original target, which is exactly what you're looking for!

So you can write addPropToArray() like this:

const addPropToArray = <T extends unknown>(x: T[]): T[] & { prop: number } => {
  return Object.assign([...x], { prop: 3 });
};

without type assertions or //@ts-ignore comments.

Note that if you ever do have to choose between type assertions and //@ts-ignore, choose type assertions. They both have the ability to stop errors, and neither is safe. But type assertions are limited in scope and telling the compiler to treat a certain value as if it were a certain type, while //@ts-ignore is merely suppressing the error message and can have strange nonlocal side effects. Using a type assertion is like pushing the "temporary hush" button on your smoke detector so you can cook dinner in peace, while using //@ts-ignore is like removing the smoke detector's battery completely.


Anyway, let's make sure it works:

const z = addPropToArray(["a", "b"]);
console.log(z) // ["a", "b"]
console.log(z.prop) // 3

Looks good.

Playground link to code

  • Related