how come this returns undefined

The type of partPresignedUrl is:
{ url: string; partNumber: number; }[]
{ url: string; partNumber: number; }[]
Ie. its an array of objects. The code for the image:
for (const { url, partNumber } of partPresignedUrls) {
console.log("initial src object:", partPresignedUrls[0])
console.log("url: ", url);
console.log("partNumber: ", partNumber);
}
for (const { url, partNumber } of partPresignedUrls) {
console.log("initial src object:", partPresignedUrls[0])
console.log("url: ", url);
console.log("partNumber: ", partNumber);
}
this really shouldn't be any different from this https://www.typescriptlang.org/play?#code/MYewdgzgLgBAtgTwIICcUwLwwNoG8YCuKANgFwwBEAjNISRQDQwAOAhilAHIFwBGApinJUYAXyb4iZSgCYwAEzrFGLdlx4ChMGWIlLyFAMwpFUlWw7c+g8obEBdANwAoZwDMQ6ABShIsSSRMFurW6KIwIG7wyGgAlDC4zjDJML4QIMT8AHTEIADmXlJBalaasc6iQA
TS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
33 Replies
barry
barry15mo ago
Not undefined for me
Mordi
Mordi15mo ago
if you are talking about the ts-playground link, yeah that one behaves as expected/wanted, but the console.logs in the image are the ones I dont get it shows the values if I log the the whole array, but returns undefined if I try to select the values of a specific object
Tom
Tom15mo ago
its a little tough cause you didnt paste the actual code youre running cause its probably something small
Mordi
Mordi15mo ago
the code blocks is the code that I am running, but maybe I should create a codesandbox but at the same time I doubt that something RHF or useCallback is the culprit
const onSubmit: SubmitHandler<formSchemaType> = useCallback(async (formInput) => {
....

for (const { url, partNumber } of partPresignedUrls) {
console.log("initial src object:", partPresignedUrls[0])
console.log("url: ", url);
console.log("partNumber: ", partNumber);
}

....
}, [...] }
const onSubmit: SubmitHandler<formSchemaType> = useCallback(async (formInput) => {
....

for (const { url, partNumber } of partPresignedUrls) {
console.log("initial src object:", partPresignedUrls[0])
console.log("url: ", url);
console.log("partNumber: ", partNumber);
}

....
}, [...] }
Tom
Tom15mo ago
uhh i qactually think it might be RHF does a bunch of very complicated things to get the typings to work for instance
Tom
Tom15mo ago
Tom
Tom15mo ago
this is from my code almost all of those fields are actually nonoptional, but they do a bunch of weird stuff so every field (and every subfield) in the defaultValues obj IS optional
Mordi
Mordi15mo ago
but RHF doesn't mess with useStates does it? in my case partPresignedUrls is just useState object. ie:
const [partPresignedUrls, setPartPresignedUrls] = useState<
{ url: string; partNumber: number }[]>([]);
const [partPresignedUrls, setPartPresignedUrls] = useState<
{ url: string; partNumber: number }[]>([]);
Mordi
Mordi15mo ago
Might have found it, UPDATE: was not it
Tom
Tom15mo ago
no it doesnt, but my point was just its kinda hard to tell whats going on without the full context
Mordi
Mordi15mo ago
I will make a codesandbox
Tom
Tom15mo ago
couldnt you just paste the whole component?
Mordi
Mordi15mo ago
its 360 lines
Tom
Tom15mo ago
thats fine
Mordi
Mordi15mo ago
Tom
Tom15mo ago
whats the type for setPresignedUrl in BasicDropzone? i think youre lying to typescript somewhere in that typedef.
Tom
Tom15mo ago
cause your problem is here:
Tom
Tom15mo ago
the code for that is console.log("initial src object:", partPresignedUrls[0]) so partPresignedUrls[0] shouldnt be an array
Mordi
Mordi15mo ago
I am:
presignedUrl: { [partNumber: number]: File; }[];
// TODO: add the correct typings to these
setPresignedUrl: any//(e: any) => void;
uploadId: string[];
setUploadId: any//(e: string[]) => void;
presignedUrl: { [partNumber: number]: File; }[];
// TODO: add the correct typings to these
setPresignedUrl: any//(e: any) => void;
uploadId: string[];
setUploadId: any//(e: string[]) => void;
Tom
Tom15mo ago
yes any theres the issue theres a thing you can do to get the type for the setter in a setState(), but i pretty much always just make a regular setter function like setPresignedUrl: {(newPresignedUrl: string): void} and then pass that in and then the logical problem in DropZone is that youre setting an array instead of an array element does that make sense?
Mordi
Mordi15mo ago
is this about the setPresignedUrl inside the dropzone-component?
Tom
Tom15mo ago
yeah somewhere in your DropZone component you are calling the setter that was passed in thru props with an array of 1 thing instead of the individual object itself
Mordi
Mordi15mo ago
Tom
Tom15mo ago
in general my quick opinion that nobody asked for is that you should almsot never leave the types as TODOs. the types are there to help you while youre writing the code again, its a bit hard to help cause the context is missing. like obviously theres bits of code missing from what you pasted because the useState() you pasted has the name setPartPresignedUrls and then the code below has setPresignedUrls and for these kinds of issues the devil really is in those details
Mordi
Mordi15mo ago
I did keep the names different, didn't think it mattered as long as the types were the same but then I ended up putting any
Tom
Tom15mo ago
my point isnt about the names so much. the names just make it clear that there is stuff missing between the useState() you pasted and where you are using it which might be relevant
Mordi
Mordi15mo ago
oh yeah, I just looked at the init of the state and didn't think of the connection to the dropzone-component
Mordi
Mordi15mo ago
oh it's not though, one index per file, and for each file we may or may not have to split it into multiple chunks, depends on file size. example: 1 large file, 1 small file
Mordi
Mordi15mo ago
so setPresignedUrl: {(newPresignedUrl: string): void} doesn't work. It has to be something like: setPresignedUrl: {(newPresignedUrl: {url: string, partNumber: number}[]): void}, but what I just wrote is not valid ts
Mordi
Mordi15mo ago
I am struggling to get the types right. I am currently just using the types from the useState hook:
const [partPresignedUrls, setPartPresignedUrls] = useState<{ url: string; partNumber: number }[]>([]);
const [uploadId, setUploadId] = useState<string[]>([]);
const [partPresignedUrls, setPartPresignedUrls] = useState<{ url: string; partNumber: number }[]>([]);
const [uploadId, setUploadId] = useState<string[]>([]);
meaning, the props to the dropzone-component looks like this:
const BasicDropzone = ({
presignedUrl,
setPresignedUrl,
uploadId,
setUploadId
}: {
presignedUrl: {
url: string;
partNumber: number;
}[];
setPresignedUrl: Dispatch<SetStateAction<{ url: string; partNumber: number; }[]>>;
uploadId: string[];
setUploadId: Dispatch<SetStateAction<string[]>>;
}) => { ... }
const BasicDropzone = ({
presignedUrl,
setPresignedUrl,
uploadId,
setUploadId
}: {
presignedUrl: {
url: string;
partNumber: number;
}[];
setPresignedUrl: Dispatch<SetStateAction<{ url: string; partNumber: number; }[]>>;
uploadId: string[];
setUploadId: Dispatch<SetStateAction<string[]>>;
}) => { ... }
I can set the uploadId with no problem:
setUploadId((prevState) => [...prevState, res.uploadId]);
setUploadId((prevState) => [...prevState, res.uploadId]);
But if I try do the same with setPresignedUrl:
setPresignedUrl((prevState) => [...prevState, urls]);
setPresignedUrl((prevState) => [...prevState, urls]);
I get an ts(2345) error message, with the full text being:
Argument of type '(prevState: { url: string; partNumber: number; }[]) => ({ url: string; partNumber: number; } | { url: string; partNumber: number; }[])[]' is not assignable to parameter of type 'SetStateAction<{ url: string; partNumber: number; }[]>'.
Type '(prevState: { url: string; partNumber: number; }[]) => ({ url: string; partNumber: number; } | { url: string; partNumber: number; }[])[]' is not assignable to type '(prevState: { url: string; partNumber: number; }[]) => { url: string; partNumber: number; }[]'.
Type '({ url: string; partNumber: number; } | { url: string; partNumber: number; }[])[]' is not assignable to type '{ url: string; partNumber: number; }[]'.
Type '{ url: string; partNumber: number; } | { url: string; partNumber: number; }[]' is not assignable to type '{ url: string; partNumber: number; }'.
Type '{ url: string; partNumber: number; }[]' is missing the following properties from type '{ url: string; partNumber: number; }': url, partNumberts(2345)
Argument of type '(prevState: { url: string; partNumber: number; }[]) => ({ url: string; partNumber: number; } | { url: string; partNumber: number; }[])[]' is not assignable to parameter of type 'SetStateAction<{ url: string; partNumber: number; }[]>'.
Type '(prevState: { url: string; partNumber: number; }[]) => ({ url: string; partNumber: number; } | { url: string; partNumber: number; }[])[]' is not assignable to type '(prevState: { url: string; partNumber: number; }[]) => { url: string; partNumber: number; }[]'.
Type '({ url: string; partNumber: number; } | { url: string; partNumber: number; }[])[]' is not assignable to type '{ url: string; partNumber: number; }[]'.
Type '{ url: string; partNumber: number; } | { url: string; partNumber: number; }[]' is not assignable to type '{ url: string; partNumber: number; }'.
Type '{ url: string; partNumber: number; }[]' is missing the following properties from type '{ url: string; partNumber: number; }': url, partNumberts(2345)
Mordi
Mordi15mo ago
Should I post a new question titled something like "typedefs & usage of useStates with object arrays" ?
Tom
Tom15mo ago
you should probably post a new question, but ill make a couple of suggestions 1) in general, keep the useState setter inside the component it came from. when your dropzone needs to make a change just have it call an onChange() function (or whatever name) with the new data. then you dont need to pass around sophisitcated types between classes. 2) the error message is saying that sometimes you are setting it with an element and sometimes you are setting it with an array that is what the type error is about. you should make sure you are always setting an array
Mordi
Mordi15mo ago
I don't see how I can create the onChange/updater function without getting the same type errors. so in the same component as the component as the state was initialized in we create a onChange function:
const urlUpdateHelper = (urls: {url: string, partNumber: number }[] | {url: string, partNumber: number}) => {
setpartPresignedUrls((prevState) => [...prevState], urls]);
}
const urlUpdateHelper = (urls: {url: string, partNumber: number }[] | {url: string, partNumber: number}) => {
setpartPresignedUrls((prevState) => [...prevState], urls]);
}
this still returns the same error message What I did in the dropzone-component was:
const Dropzone = ({setPresignedUrl}: {setPresignedUrl: (e: { url: string; partNumber: number; } | { url: string; partNumber: number; }[]) => void;}) => {
...
setPresignedUrl(urls);
}
const Dropzone = ({setPresignedUrl}: {setPresignedUrl: (e: { url: string; partNumber: number; } | { url: string; partNumber: number; }[]) => void;}) => {
...
setPresignedUrl(urls);
}
and for passing props:
<Dropzone setPartPresignedUrl={(e) => urlUpdateHelper(e)} ..
<Dropzone setPartPresignedUrl={(e) => urlUpdateHelper(e)} ..
creating a new question/thread so that anyone in a similar situation doesn't have to go through 50+ messages new thread here: https://ptb.discord.com/channels/966627436387266600/1093227515524431976