-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
[documentation] Clarify the semantics of void #20006
Comments
We need to update the spec. but in general, the only useful location for a |
I'm curious how this is advantageous. Is there an example where the special semantics of
If it's wrong, why is it possible to do without error? And why is it wrong? Like |
just add |
One thing to keep in mind is that It seems wrong to allow assigning a function that returns I do not think it should be an error to use I do agree that under I think |
I'm not advocating that var x: number | undefined = voidFunction(); should ever work! Only that var x: {} | undefined | null = voidFunction(); should work. I'm unable to imagine a way in which this could be abused or lead inadvertently to bugs. I also think that there's no harm in saying const x: void = 3 How could that possibly lead to problems? You can't actually do anything with such an |
by accident? by refactoring?
the assignment above is as good as an expression statement which is a disaster for the value that cannot be thrown away:
callbacks with return |
What did you mean here by
I think this is a subjective issue.. there are trade offs for all these design decisions. we made a decision to make
I think this wrong. |
That's the whole problem; it's not known what is meant by
But that's not true. A const foo: () => void = () => 3 so at runtime one has to expect that a |
what is the practical benefit of making |
this is wrong, try it:
read closely my example the difference is subtle |
It has a meaning.. a lack of a return value.. you want it to be the top type, but it is not.. :) |
No. a void-returning function accepts an assignment from a function that can return anything.. since it is ok to ignore return values if they exist. but a a void-return function as authored will always return undefined.. |
If all I know is that I have an const f: () => void = () => 3; in which case it will most definitely not return const x = f(); So, without using any casts to subvert the type system, the At the very least can we agree that this should work? const foo: {} | undefined | null = voidFunction(); |
no we cant, void doesnt have values, so there no way to get a value of the type (however you can shove an undefined value into an empty set called |
@Aleksey-Bykov |
(Well, I shouldn't say it's the only empty type... there's plenty of others, e.g. |
in you miraculous world the type the fact that you are allowed to declare a variable of
|
typescript lies about types thats in its nature, its his strong and weak side, c# cant lie about types, and its very dumb and clumsy |
now the contradiction of having a variable of the back in a day both null and undefined types were like that in TS: although the type existed in the internals of TS, there was no way to declare a value of it, because the type didn't have any syntax point is , for any true empty type, there must be no way to declare a value of it, hence neither void nor never are trully empty |
couple more thoughts in that direction: #4183 (comment) |
This is a bad idea. The
Yes, TypeScript has plenty of ways to subvert the type system, but this is not something to be celebrated. If there is an opportunity to remove one of these lies and increase the logic and symmetry of the type hierarchy without decreasing expressiveness, it's worth doing IMO. |
I disagree. I wouldn't want this to be legal: // Takes any value
function myStringify(x: {} | undefined | null) { ... }
// a value of type 'void' shouldn't be used where a 'real' value is expected
myStringify(voidFunction()); I think the relation to A value of type
I think it was disallowed to even declare variables of type I get the desire to have a top type for the purposes of input positions (though am still unclear on why |
Ok, but why not? I just fail to see what could go wrong. The function has declared itself capable of handling literally any value, and the type system would enforce that it made appropriate checks before treating it like anything more specific. What's gained by forbidding this?
This is a good way of describing it, and (getting back on topic for this issue) might be a good starting point for better documentation. I think I understand the intended semantics of |
i disagree with ryan, what is observed? what is instructive? these words imply some heurstic / protocols / rituals (approved or disapproved by the mighty and only TS authority terrible but just) really? at the end of the day these types either just serve their purposes where they make sense or kept on the shelf when they dont never has a few places of applicability, so does void anyone is free and welcome to abuse them as much as they want as long as it makes them happy any think mysterious and sacral meanings only confuse people more |
practically though type void only makes sense where its unique properties are beneficial for solving a problem same with never these types are only as good or bad as you can make use of them thank to their properties, there is nothing else to it |
The fact that JS forces a return value to appear behind the scenes is a rather unfortunate result of its design that I think is beneficial to be able to ignore, much like most of our type system. It's instructive to see how C treats |
My understanding is that the main goal of TypeScript is to model the actual semantics of JavaScript (even the ones that may have been odd design choices) while preventing bugs. Again I ask, what could go wrong here?
That's not an apt comparison, because those are the semantics of C, not JavaScript. Dereferencing
How could you think it returns something useful? If the return type means "this could be literally anything" that's tantamount to saying that the result is meaningless. But saying that you can't subsequently inspect that value, however meaningless it might be, is not faithful to the semantics of JavaScript. And it leads to a wart of a type in |
I think this is the point of contention here. I am saying it means |
Result callbacks are a real-world case where it's meaningful to end up in situations where you have a type Callback<R> = (err?: any, result?: R) => void;
type Handler<A, R> = (arg: A, context: HandlerContext, cb: Callback<R>) => void;
type SomeHandler = Handler<SomeArg, void>;
// lots of other *Handler types
const case1: SomeHandler = (arg, context, cb) => {
...;
cb(null, { ignored: true }); // ignored, should probably error
};
const case2: SomeHandler = wrapAsyncHandler(async (arg, context) => {
...;
return { ignored: true }; // ignored, should still probably error.
});
const asyncSomeHandler = async (arg: SomeArg, context: HandlerContext) => { // note result type is inferred.
...;
return { ignored: true }; // will end up ignored, but shouldn't error.
};
const case3: SomeHandler = wrapAsyncHandler(asyncSomeHandler); // the same as case2, but should have no error when ignoring the result
const case4: SomeHandler = (arg, context, cb) => {
asyncSomeHandler(arg, context).then(result => {
cb(null, result); // basically the same as case3, but explicitly passing void to void - could be due to the handler declaration updating, refactoring, copy-paste. Is this an error?
});
};
function wrapAsyncHandler<A, R>(
asyncHandler: (arg: A, context: SomeContext) => R | Promise<R>,
): Handler<A, R> {
return (arg, context, cb) => {
new Promise<R>(resolve => {
resolve(asyncHandler(arg, context)); // Could end up passing void to void, but generic, so who cares?
}).then(
result => cb(null, result),
reason => cb(reason),
);
};
} (This example is basically the AWS Lambda runtime environment, and I've written every one of those cases!) Perhaps I do agree that
|
as of today there is no super type of anything and everything, a few month ago it was so called if you need safer |
Fortunately, following the links it looks like we might get a real top type! #21368 (comment) |
it's less nonsense than
|
const handler: Handler<X, undefined> = () => {}; // error: 'void' is not assignable to 'undefined'. Wat?
|
i am looking for basic sanity:
|
You literally already typed that. I know what you want, and I already explained why I think |
i did many times, so what? you had a point about |
apart from and the price to pay isn't that high you can just use as simple as it seems it is not a very popular measure because requires more typing, so a path of least resistance was taken back to "must be" how do you propagate it through the code?
|
not sure what you point is, i read the non-goals and i know how to get to the items i opened, here i question the decision being made about |
The points would be:
|
|
First point was referring to this:
If you do want that, give Elm or https://www.npmjs.com/package/tslint-immutable#no-expression-statement a try. (I'm not being sarcastic, FP is great, just not something TS will ever enforce) Not sure what you mean with the second and third points?
|
In the handbook, it's stated that
and
The latter statement is not true in any useful sense;
null
is not assignable tovoid
with--strictNullChecks
enabled, i.e. it's no more assignable tovoid
than any other type, so might as well not mention it.The former is (maybe?) approximately true, but how about saying exactly what it means? I for one have never been 100% sure and would love to have some clarity. For instance, the
unknown
type,is supposed to be a "top" type, i.e. any value should belong to it. And yet,
void
is not assignable to it.Why is that? According to the above, only
undefined
(andnull
, if no--strictNullChecks
) inhabitvoid
, so why is it not assignable tounknown
?Since there is no longer a current language specification, it seems the best available documentation is to be found at https://www.typescriptlang.org/docs/home.html, so it would be great if it could give a crisp definition of
void
!The text was updated successfully, but these errors were encountered: