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;
})();
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.