Update 1️⃣
As per comments, I did create a standalone example of what seems to be the same situation:
interface Person {
name: string;
age: number;
}
type People = Person[];
const p: Person = {
name: "Mark",
age: 39,
};
const people: People = [p];
const anotherP = {
...people[0],
age: 40,
};
However...this doesn't create any issue!
So, now the issues might be from the actual types from: import type { Meta, StoryObj } from '@storybook/react';.
Anyway, as seems to be the way to go for this particular situation.
Consider:
const meta = {
args: {
items: [
{
isValid: null,
stepNumber: '1',
text: 'Customer Information',
children: 'Content',
},
],
},
If I remove 1️⃣ of the s, I get the expected pushback from TS. This confirms that the types are all good in the provided snippet.
However, further along in the code I have:
export const Valid: StoryObj<typeof meta> = {
args: {
items: [
{
...meta.args.items[0],
isValid: true,
},
],
},
};
In this part specifically:
{
...meta.args.items[0],
isValid: true,
},
TS says, "Type { isValid: true; stepNumber?: string | undefined; text?: string | undefined; children?: string | undefined; } is not assignable to type CheckoutAccordionItemProps.
Types of property stepNumber are incompatible.
Type string | undefined is not assignable to type 'string'.
Type undefined is not assignable to type string..."
For more visibility ️, here's this:
export interface CheckoutAccordionItemProps {
isValid: boolean | null;
stepNumber: string;
text: string;
children: ReactNode;
}
To summarize, even though:
args: {
items: [
{
isValid: null,
stepNumber: '1',
text: 'Customer Information',
children: 'Content',
},
],
},
is fine, the act of spreading: ...meta.args.items[0], seems to introduce undefined possibilities.
This can be resolved by using: ...(meta.args.items[0] as CheckoutAccordionItemProps),.
Why do we need to use this ugly type assertion as? Spreading should not have compromised the original type information.
Whenever we spread an object with TypeScript, it doesn't just take the values and keys at face value. Instead, TypeScript tries to represent the types of the output object as accurately as possible, including the possibility that some keys might not exist on the source object even if they do at runtime, resulting in
undefinedbeing a 'possibility.'TypeScript interprets this as "Take all properties from
meta.args.items[0]and add/overwrite theisValidproperty with the valuetrue". But TypeScript doesn't guarantee that all properties ofmeta.args.items[0]exist (even if we know that they do). It adds the possibility ofundefinedto each of them.This is a safe behavior because in JavaScript, if you were to spread an object that didn't have one of the expected keys, the resulting object would indeed have that key set to
undefined.We're forced to use a type assertion to tell TypeScript, "Trust me, I know what I'm doing".