Repository navigation
await Promise.all fails to resolve due to optional chaining in JSX #52475
Description
Activity
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.
- changed the title
[-]await Promise.all fails to resolve due a toString call inside JSX[/-][+]await Promise.all fails to resolve due a optional chaining in JSX[/+]on Jan 28, 2023 - changed the title
[-]await Promise.all fails to resolve due a optional chaining in JSX[/-][+]await Promise.all fails to resolve due to optional chaining in JSX[/+]on Jan 28, 2023 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.
- create a string variable from column?.toString
- call
column?.toString()(with or without the if guard) (with or without the optional chaining) - put the myString variable inside the div
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.
RyanCavanaugh commented
on Jan 30, 2023 MemberMore actionsMinimal 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
Reacted by Jacob EndersRyanCavanaugh commented
on Jan 30, 2023 MemberMore actionsOK, 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:
"dependencies": { "react": "17.0.2", "react-dom": "17.0.2" }, "devDependencies": { "@types/react": "17.0.53", "@types/react-dom": "17.0.18", "@vitejs/plugin-react": "2.2.0", "typescript": "4.9.4", "vite": "3.2.5" }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.
Reacted by Jacob Enders- addedNeeds InvestigationThis issue needs a team member to investigate its status.This issue needs a team member to investigate its status.
on Jan 30, 2023 Ryan Cavanaugh (@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;
Reacted by Ryan Cavanaugh- addedBugA bug in TypeScriptA bug in TypeScriptHelp WantedYou can do thisYou can do thisand removedNeeds InvestigationThis issue needs a team member to investigate its status.This issue needs a team member to investigate its status.
on Jan 31, 2023 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 } }Was able to reproduce it in a single file, and have it in the playground.
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; }
Seem like it was the mapping that wasn't allowing us to repro it in a single file.

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; }
RyanCavanaugh commented
on Feb 1, 2023 MemberMore actionsJacob Enders (@Fish1) big thanks for helping get to a short repro and Mateusz Burzyński (@Andarist) for the fast fix!
Amazing work! Thanks everyone!
It was so bizarre that I couldn't resist debugging this 😅
Reacted by Conrad Buck- locked as resolved and limited conversation to collaborators
on Oct 22, 2025














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