Comments (5)
This is just a bug. We have special logic in contextual typing to ensure that a function that would normally be inferred as void
return undefined
instead if needed:
type FN = () => undefined;
// Not an error
const fn1: FN = () => {
};
Something with the union with Promise is messing it up, probably because we also have logic to make the Promise case in specific also work:
type FN = () => Promise<undefined>;
// Also not an error
const fn1: FN = async () => {
};
Unions with other values don't seem to have this problem
from typescript.
Interestingly if the function is async now Promise<undefined>
is always the return type, even if I omit the return
statement entirely, which seems pretty inconsistent.
from typescript.
It's related to this 5.1 change. The linked PR mentions that:
Functions with an explicitly specified return type undefined aren't required to have return statements (similar to functions with an explicitly specified return type of void or any). Note that this does not apply to functions with union return types that include undefined.
I'm not sure what's the exact reason behind this but I find it confusing and inconsistent too. I get it that it might be easy to forget a return value in such cases. It feels like a potential lint rule for people who prefer explicit return undefined
. It's surprising that the typechecker complains here. At the very least the error message could mention how to fix this problem because at the moment it's not obvious what to do about it.
This is a reason why people write weird things when they actually want to allow those implicit undefined when the conceptual return type is meant to be a union that includes undefined
(taken from @types/react
):
declare const UNDEFINED_VOID_ONLY: unique symbol;
type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never };
type EffectCallback = () => void | Destructor;
Whereas if undefined
could be implied when dealing with unions this would be much simpler:
type Destructor = () => void;
function useEffect(cb: () => undefined | Destructor) {}
useEffect(() => {
// it would be perfect if this could be implied
return undefined;
});
// errors as expected
useEffect(() => {
return 10;
});
useEffect(() => {
// it works just fine
return () => {};
});
useEffect(() => {
return () => {
// we don't care about the return type here so it's allowed
return "foo";
};
});
from typescript.
Unions with other values don't seem to have this problem
Maybe we are talking about some other behaviors. I think the OP is reporting the same problem as the one showcased here:
type FN = () => string | undefined;
// Type '() => void' is not assignable to type 'FN'.
// Type 'void' is not assignable to type 'string | undefined'.(2322)
const fn1: FN = () => {
return;
};
// ok
const fn2: FN = () => {
return undefined;
};
// Not all code paths return a value.(7030)
const fn3: FN = () => {
if (Math.random()) {
return "";
}
};
The code that "converts" the inferred void
to undefined
is here and it doesn't use someType
(that would handle unions) to check if contextualReturnType
contains an undefined
type.
Unless you are saying that both of those should be errors:
type FN = () => Promise<undefined> | undefined;
// Type '() => void' is not assignable to type 'FN'.
// Type 'void' is not assignable to type 'Promise<undefined> | undefined'.(2322)
const fn1: FN = () => {
return;
};
// ok
const fn2: FN = async () => {};
IIUC, this is not what OP's post is about though so the added "Bug"/"Help Wanted" labels make it confusing to me.
from typescript.
What I mean is that if the contextual type itself i a union with other types, that doesn't spoil the pot:
type FN = 42 | (() => undefined);
// Not an error
const fn1: FN = () => {
};
IIUC, this is not what OP's post is about though so the added "Bug"/"Help Wanted" labels make it confusing to me.
To clarify, this code should not have an error:
type FN = () => Promise<undefined> | undefined;
const fn1: FN = () => {
return;
};
Seems like using someType
in that linked code might be the right fix
from typescript.
Related Issues (20)
- Smarter String includes/endsWith/startsWith using template literal type predicates HOT 3
- Typed key accessor fails to infer type when used with generic HOT 2
- Upstream file not correctly recognized as output of other project when using path mappings HOT 9
- [nightly][regression] Wrong generic parameter inferred for constructed class instance HOT 1
- [nightly][regression] Some emitted imports are syntactically invalid HOT 5
- Array of numbers wrong sort HOT 9
- Mistake in README file
- Inconsistent Behavior with Equality Check Using `Pick<Readonly<T>, K>` in Mapped Type HOT 2
- Improve Omit field type check HOT 1
- Class parameter property with initializer before required property emits non-nullable parameter for declaration emit
- TypeScript emits incorrect type for setters in JSDoc
- Function returning `never` in branch doesn't narrow type if it's not explicitly annotated HOT 3
- Type Intersection incorrect for optional parameters in functions with `exactOptionalPropertyTypes: true`. `undefined` type is flip based off Intersection types
- Inconsistent tsc behavior on TS 5.4 HOT 3
- VSCode TypeScript extension writes tracing to the wrong directory on Mac OS HOT 1
- computed property name in an interface error when using `[]` already HOT 3
- Proposal: Type-side `instanceof` keyword and functionality HOT 8
- tsserver.js CPU/Memory Spike HOT 3
- Update README.md, CONTRIBUTING.md and SECURITY.md to reduce mistakes HOT 4
- [NewErrors] 5.5.0-dev.20240414 vs 5.4.5 HOT 30
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typescript.