Repository navigation
add type-guard for void #1806
Description
Activity
My impression is this is an attempt to hack in making an implementation require null checks. This probably isn't the route we'd go if we want to enable that sort of feature (which I understand the desire for). As far the unique behavior of
voidnote this bit from the language spec on void that has persisted for many versions now (I thought we'd removed this note):NOTE: We might consider disallowing declaring variables of type Void as they serve no useful purpose. However, because Void is permitted as a type argument to a generic type or function it is not feasible to disallow Void properties or parameters.
fdecampredon commented
on Jan 26, 2015 AuthorMore actionsThe point is not really to force null check (and in my example the typeguard that compare to undefined would not prevent us from null) but to have a better subtype than
{},{}which still inherit from the Object behavior, while this feature is not invaluable I still like the effect.So how would this differ from your proposal for
objectas a primitive? #1809 It seems like you either want to type func2's argument asobjector be able to use{} | voidfor a similar effect?fdecampredon commented
on Jan 27, 2015 AuthorMore actionsIt's quite different, let me give an example:
type mixed = {} | void; function acceptMixed(arg: mixed): void { if (typeof arg === 'string') { arg.split(...); //valid } arg.hasOwnProperty('hello') // error console.log(arg) // no error } acceptMixed('string') // no error acceptMixed(3) //no error
anything can be assigned to
mixed({} | void), but unlike{}the compiler does not implies any method fromObjectfor this type , allowing a certain safety (for example such method would accept resultObject.create(null)with safety).
Note that this is actually the 1.4 typescript behavior.the
objecttype is quite different, every primitive type (exceptany,null,void) are not assignable toobject, and like for other type the compiler infer that it hasObjectprototype methods :function acceptObject(arg: object): void { arg.hasOwnProperty('hello') // no error } acceptMixed('string') // error acceptMixed(3) // error
If we added type guard for 'undefined', we could narrow type to
{}instead of narrowing it to a subtype likestringetc ...However like I said that's not something that I absolutely want, I just think that the type
mixeditself has quite interesting property, and it's more a nice trick than anything else.Agree there should be a nullish type guard:
function accept(arg: string): void { if (arg == null) { // arg narrows to void } else { // arg narrows to string!null!undefined arg = null // error } }
Some related thoughts at the bottom of #1892
The reasoning for removing 'void' is it's already a type that is included in every union type.
type uniona = string | number; // infers string|number|void type unionb = string | number | void; // void here is *not* needed since 'string | number' -> implies string|number|void function accept(arg: string): void { if (arg == null) { // arg narrows to void } else { // arg narrows to string!null!undefined arg = null } } function a(arg: uniona): void { 'use strict' if (arg == null) { // arg narrows to void } else { // arg narrows to string|number&!null!undefined arg = null // error } } function b(arg: unionb): void { if (arg == null) { // arg narrows to void } else { // arg narrows to string|number&!null!undefined arg = null } }
- addedSuggestionAn idea for TypeScriptAn idea for TypeScript
on Mar 24, 2015 I would see this as part of nullable/non-nullable types support (#185); non-nullable types is something that we did not dismiss, but it is not on the roadmap for the short term.
so what would be the use cases here?
Dan Quirk (@danquirk)'s reply in #1806 (comment) would be my opinion as well. I do not think this is the intention of void as a type; and I have not seen compelling use cases so far. I would say the issue should be closed.
Without a void type guard, how would you satisfy this:
declare var k: (() => string)|void; if (k !== null || k !== undefined) k(); if (typeof k !== 'undefined') k(); if (typeof k == 'Function') k();
Every attempt to invoke
kabove fails with:Cannot invoke an expression whose type lacks a call signature.That seems like exactly the type of pattern I described in #1806 (comment)
I think you are right, the
Cannot invoke expression ...error message doesn't specify if the type can be reduced to a function or not (too general vs. not a function).Unlike my former example where the type can be reduced, the following (if I am not mistaken) can not be reduced because generics do not apply arguments to specialized overloads?
declare function foo(name: 'bar'): (i: number) => string; declare function foo(name: 'manchu'): (key: string) => string; declare function foo(name: string): void; foo('bar')(1); // no error, returns string foo('manchu')('wok'); // no error, returns string foo('bar')('?'); // "Arg of type string ... not a number" foo('manchu')(1); // "Arg of type number ... not a string" foo('?')(); // "Cannot invoke expr ... no call sig" function factory<N extends string, T>(name: N, arg: T): string { return foo(name)(arg); // "Cannot invoke expr ... no call sig" }
It does seem quite contrived, but I would like to be able to guarantee (at compile time) that calling
factorywithbarrequires a number, withmanchurequires a string and anything else is a compile error.looks like a duplicate of #3076.
- addedDuplicateAn existing issue was already createdAn existing issue was already created
on Dec 9, 2015 - locked and limited conversation to collaborators
on Jun 18, 2018
There is a type that is quite interesting in typescript
void | {}, this type has some unique behavior, everything can be assigned to this type, but the compiler does not infer any property for this type unlike for{}that inherits the basic object properties, the difference with{}is subtile but still pretty interesting:However there is not typeguard for
void, it could be interesting to add some type guards for this case:typeof val !== 'undefined'val !== undefinedval != undefinedval != null