Repository navigation
Promise.catch null vs null as null #44913
Copy link
Copy link
Closed
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issueRescheduledThis issue was previously scheduled to an earlier milestoneThis issue was previously scheduled to an earlier milestone
Milestone
Description
Bug Report
🔎 Search Terms
🕗 Version & Regression Information
- This is a crash
- This changed between versions ______ and _______
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about _________
- I was unable to test this on prior versions because _______
⏯ Playground Link
Playground link with relevant code
💻 Code
Version 4.4.0-dev.20210705
export function f1()
{
return Promise.resolve(1).catch(e => null)
}
export function f2()
{
return Promise.resolve(1).catch(e => null as null)
}
// @ts-expect-error
f1().then(v => v.split('')) // <====== should is `number` but `any`
// @ts-expect-error
f2().then(v => v.split('')){
"compilerOptions": {
"module": "commonjs",
"target": "es2019",
"jsx": "preserve",
"moduleResolution": "node",
"allowJs": false,
"allowUmdGlobalAccess": false,
"sourceMap": true,
"inlineSourceMap": false,
"inlineSources": true,
"declaration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"strict": false,
"strictBindCallApply": true,
"strictNullChecks": false,
"strictFunctionTypes": true,
"noImplicitThis": true,
"noImplicitOverride": true,
"preserveConstEnums": true,
"forceConsistentCasingInFileNames": true,
"incremental": true,
"noErrorTruncation": true,
"removeComments": true,
"traceResolution": false,
"newLine": "lf",
"esModuleInterop": true,
"importHelpers": true
}
}
🙁 Actual behavior
// @ts-expect-error
f1().then(v => v.split('')) // <====== should is `number` but `any`
🙂 Expected behavior
// @ts-expect-error
f1().then(v => v.split('')) // <====== should is `number`
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issueRescheduledThis issue was previously scheduled to an earlier milestoneThis issue was previously scheduled to an earlier milestone

Activity
fatcerberus commentedon Jul 8, 2021
I think this happens because in the absence of a type annotation,
nullgets inferred asany, even understrictNullChecks.rbuckton commentedon Oct 23, 2023
That's not quite true. When outside of
--strictNullChecksthere are twonulltypes: A non-wideningnulltype and a wideningnulltype. The wideningnulltype is returned whennullis used as an expression, while the non-wideningnulltype is returned whennullis used as a type. At any point where we widen (such as when we widen"foo"tostring), the wideningnullis either removed from any union it is contained in, or is converted toany, while the non-wideningnullis only removed from unions if there are any other non-widening types. When inside of--strictNullChecks, there is only ever onenulltype, which is the non-wideningnulltype. The same is also true forundefined.The example you show here isn't quite correct, as the type you see when you hover over
xisn't the inferred type, it is the type resulting from control flow analysis:Playground
In the example from the OP, you can see that both
numberandnullare preserved in--strictNullChecks:Playground
The reason why you see
anyis due tonullbeing the only possible return value of.catch. As a result, it widens toanyas the type escapes the body. You can see that here, though I had to replace the arrow functions withfunctionexpressions so I could have something to point the^?comment at:Playground
As a result, when constructing the resulting
Promisetype returned fromcatchinf1, the result isnumber | any, which is reduced toany.Currently, this is a limitation of running outside of
--strictNullChecks, but I'm considering whether there are any changes we can make here.rbuckton commentedon Aug 16, 2024
I've put up #59661, which reports an error due to the implicit widening from
nulltoany.