-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
await Promise.all fails to resolve due to optional chaining in JSX #52475
Comments
This error seems to be caused by the optional chaining of the ReactNode type. The optional chain can exit if it has an if statement guard, and it is fine. It's only an issue when the code is depending on the optional chain. Also, I can only seem to get this error when working with the ReactNode type in react. |
Another test case: the optional chaining works fine, as long as you use the variable inside the JSX return of the map. And you don't use the column variable in any other pointless expression.
|
Test Case: Updating to React 18 fixes the issue. But I noticed the spot where they changed the ReactFragment type, and reverted it back to what was in React 17. This caused to issue to reappear. Here I commented out the React18 ReactFragment, and brought back the React17 ReactFragment. And the issue shows up. I then deleted the {} type from the Fragment, and the issue is resolved. |
Minimal repro I have so far: import {} from "../component/MyList";
async function myUnusedFunction() {
const getDataFetch1 = Promise.resolve(["hello", "world"])
const [data1] = await Promise.all([getDataFetch1]);
const y = data1.map((data) => {
return data.toString();
});
return { y };
} import { ReactNode } from "react";
function MyList() {
[null as ReactNode].map(column => {
column?.toLocaleString; // <-- key line
const aa = column;
});
} Trying to simplify further but this is very, very delicate |
OK, so it's just these two files import { ReactNode } from "react";
function MyList() {
[null as ReactNode].map(column => {
column?.toLocaleString; // <-- key line
const aa = column; // <-- also key line
});
} async function myUnusedFunction() {
const getDataFetch1 = Promise.resolve(["hello", "world"])
const [data1] = await Promise.all([
getDataFetch1
]);
data1.map(() => { });
} listed in that order in tsconfig, with these dependencies:
I'm incredibly curious what's going on, but would really want the react dependency stubbed out into something that still repros this before investigating further. This bug, whatever it is, is very hard to hit. |
@RyanCavanaugh Thank you so much! I think I was able to remove the React dependency. It is pushed to the "minimal" branch of my GitHub repo. Branch: https://github.com/Fish1/bughunter/tree/minimal /main.tsx async function myUnusedFunction() {
const fetch1 = Promise.resolve(['hello', 'world']);
const [data1] = await Promise.all([fetch1]);
const y = data1.map((data) => {
return data.toString();
});
return { y };
}
export default myUnusedFunction; /MyList.tsx type myType = {} | null | undefined;
function MyList() {
[null as myType].map(column => {
column?.toLocaleString;
const aa = column;
});
}
export default MyList; |
Was able to take reduce the issue a little bit more. We can simply remove the map from the MyList file. /main.ts async function myUnusedFunction() {
const fetch1 = Promise.resolve(['hello', 'world']);
const [data1] = await Promise.all([fetch1]);
data1.length;
} /MyType.ts type MyType = {} | null | undefined;
const myVar: MyType = null as MyType;
// you need the following two lines together, to make it work
myVar?.toLocaleString;
const x = myVar; I also went and reset the tsconfig to what is produced by TSC init. {
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"noEmit": true
}
} |
Since this reproduces, I did a bisect which points to #49119. |
The test, FWIW: // @strict: true
// @target: esnext
// @lib: esnext
type MyType = {} | null | undefined;
const myVar: MyType = null as MyType;
myVar?.toLocaleString;
myVar;
async function myUnusedFunction() {
const fetch1 = Promise.resolve(['hello', 'world']);
const [data1] = await Promise.all([fetch1]);
data1.length;
} |
Amazing work! Thanks everyone! |
It was so bizarre that I couldn't resist debugging this 😅 |
Bug Report - await Promise.all fails to resolve due to optional chaining inside JSX
I am sorry if this doesn't end up being a TypeScript bug, I'm just not able to replicate this without React at the moment. This feels more like a TypeScript issue? So I'll let you guys decide.
🔎 Search Terms
await Promise.all toString JSX ReactNode
🕗 Version & Regression Information
⏯ Repo Link
Sorry, I have to use a repo for this. It's a bit of a setup to create the conditions for this.
https://github.com/Fish1/bughunter
💻 Code
/src/pages/HomePage.tsx
/src/component/MyList.tsx
output of
npx tsc
🙁 Actual behavior
await Promise.all doesn't resolve the promises.
🙂 Expected behavior
I expect Promise.all() to resolve it's types correctly regardless of a variable in another file.
Strange Solutions that I have come up with
Here is a link to the TypeScript community discord, and the thread with a lots more testing.
https://discord.com/channels/508357248330760243/1068268321998372964
Video of error (incase text description wasn't clear)
Video.webm
The text was updated successfully, but these errors were encountered: