-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Optional chaining not working with void type #35850
Comments
Please follow the issue template for bugs. Here you would have to fill out terms you searched for to find existing issues. Duplicate of #35236. |
In other words, it signals not just "this function doesn't return anything", but also "this functions values are not meant to be witnessed". |
I believe my issue not about void itself. We could leave Not working, but expected to work: type Address = {
city: string
}
declare let value: Address | void | null | undefined | never
value?.city Working good: declare let value: Address | null | undefined | never
value?.city Why |
Your issue is about your type being potentially |
ok, I will try to find out my root issue. Originally, my type was Once I replaced And then, I found that I cannot use optional chaining with |
Actually, this is a big issue for functions with optional contexts. Currently, the only way to make a function that accepts an optional context is to define it as Due to this issue, it's currently impossible to use optional chaining inside the function to access properties of such context: type Foo = { bar?: { baz: string; }; };
function example(this: Foo | void, value: string) {
return this?.bar?.baz ?? value; // Currently an error here
}
example('default string'); One could argue that this is solvable by using type Foo = { bar?: { baz: string; }; };
function example(this: Foo | undefined, value: string) {
return this?.bar?.baz ?? value; // No error, cool
}
example('default string'); // "The 'this' context of type 'void' is not assignable to method's 'this' of type 'Foo | undefined'." 😕 So, currently, optional chaining is broken completely for functions with optional contexts. See more examples in this playground. I, personally, have many uses for functions like this in my libraries and projects. And I believe other people have use-cases too. |
I think this is a simple example showing that unions with void are actually helping prevent runtime problems: function simpleCallback() { }
async function advancedCallback() {
await Promise.resolve();
}
// registerCallback can be called using simpleCallback or advancedCallback for example
function registerCallback(callback: () => Promise<any> | void) {
// Does not compile unfortunately:
// callback()?.catch(console.error);
// workaround without optional chaining
let result = callback();
if (result) {
result.catch(console.error)
}
} |
@johan-gorter I think you should rather show why
http://www.typescriptlang.org/docs/handbook/basic-types.html#void
See also this comment by @RyanCavanaugh:
|
@MartinJohns, there's no arguing that But there is a case I mentioned earlier in which there's no way for In @johan-gorter's example, the function simpleCallback(): undefined { return; }
async function advancedCallback() {
await Promise.resolve();
}
// registerCallback can be called using simpleCallback or advancedCallback for example
function registerCallback(callback: () => Promise<any> | undefined) {
// EZ
callback()?.catch(console.error);
} However, in the case of |
@Raiondesu You definitely brought up a valid case for
To slightly adjust your example:
To solve your issue it would be better to allow to omit the context argument when the type is
Or alternatively accept that the caller has to pass along |
I now clearly see the problem with this. I do agree with you. But I still don't see how the context could just "suddenly" be different from the expected one (assuming we run code in And, also, type Foo = { bar?: { baz: string; }; };
function example(this: Foo | void, value: string) {
if (!this) {
return value;
}
// TS doesn't complain here!
return this.bar?.baz;
}
example('default string'); If what you're implying is indeed true, and
Should I create a separate issue then? |
This is not an intended use of |
I get it, @RyanCavanaugh, thanks, but how to deal with the function's context then? All examples listed are broken in some way or another. Even though two of them ( |
So I believe this one in particular should work without error as long as the code is in strict mode: function exampleUndefined(this: Foo | undefined, value: string) {
console.log(this);
// Works here
return this?.bar?.baz;
}
// Wrongly complains here!
// Valid JS code, though.
exampleUndefined('default string'); // logs 'undefined' |
@RyanCavanaugh but one more advantage of using |
Not sure if this is the right issue to leave this, but I think inference of void is a problem, as a function that does not return anything is inferred as I'm also not sure why |
I understand why both exist. What I don't get is why typescript would ever infer a function to return void. If void means that a function could return anything, but we won't use the value, then typescript should never infer void - how does typescript know we won't be using the return value? It seems much more sensible for a function like |
So let's say you write something like this class Base {
func() { }
}
class Derived extends Base {
func() {
return 0;
}
} Is this an illegal class hierarchy because callers of It doesn't seem like it is. It seems like Moreover, it seems nearly pointless to talk about a function whose return value is contractually obligated to be a single particular primitive value. How many functions do you call that are specified to return |
I have faced this in such situation: const p = await new Promise<{ id: number; }>((resolve) => resolve({ id: 1 })).catch(console.warn);
console.log(p?.id);
I'm not using |
@HanabishiRecca The problem isn't |
@mAAdhaTTah yeah, it can be fixed by something like: .catch((e) => { console.warn(e); return undefined; }) But this is stupid and not resolves the problem. |
Demo
playground demo
TypeScript source with nightly version 3.8.0-dev.20191224
JavaScript output
the same for both "work" and "home" properties
Bug
If you run the code above, you will see that for
u.home?.city
there are no errors.But for
u.work?.city
there is an error:Error: Property 'city' does not exist on type 'void | Address'
Expectation
Optional chaining works for both 'undefined' and 'void' types the same way.
The text was updated successfully, but these errors were encountered: