Skip to content

Suggestion: Void-protected types #6981

Closed
@dallonf

Description

@dallonf

Deep in the discussion of non-nullable types, there was a new, more practical, suggestion (credit particularly to @tejacques) of voidable types that are protected against unsafe access, while maintaining backwards compatibility. I think the suggestion is a bit too buried in that thread to be useful or noticed, so I'm creating a new issue for this proposal.

First, some definitions to make sure I'm on the same page as everyone else... for the purposes of this discussion, "void" refers to either a value of null or undefined, which will raise a TypeError if you attempt to use it in certain ways, particularly accessing a member (e.g. foo.bar) or calling it as a function (e.g. foo()). Currently in TypeScript, all types are "voidable", by this definition.

The proposal is for a new modifier (I'll use a ? prefix, similar to Flow's implementation, but it could be anything) that will make a type "void-protected". A void-protected type must be checked for existence before it can be used. For example:

function printShout(x: ?string): void {
  console.log(x.toUpperCase()); // Error: x might be void
  if (x) {
    console.log(x.toUpperCase()); // legal
  }
}

This does not affect assignments, for the sake of backwards compatibility:

function foo(x: ?string): void {
  var y: string = x;
  console.log(y.toUpperCase()); // Legal, but probably not a good idea
}

This is similar in philosophy to Java's Optional<> type. While every type in Java is nullable, it has become standard to instead use Optional<> to express this at an API level. While it doesn't completely protect against NullPointerExceptions (aka the "billion dollar mistake"), it's a step in the right direction, and some IDEs will even log warnings if you assign null to a normal, non-Optional type.

In the case of TypeScript, a --noImplicitVoidable flag could be added in the distant future, which would make all types non-voidable (that is, you could not assign null or undefined to them) by default. While this is a major breaking change, it can be mitigated by having "void-protected" types in the language for some time prior. That way, most code will already be using ? prefixes wherever void types are expected.

Finally, under this proposal, there are basically four levels of voidability, which is more relevant if this hypothetical --noImplicitVoidable flag comes to be. A lot of this has been adapted from @tejacques's response in the non-nullable thread:

interface Foo {
  w: string;
  x: ?string;
  y?: string;
  z?: ?string;
}

// alternately
function foo(w: string, x: ?string, y?: string, z?: ?string): void

w: string behaves exactly like it does currently: it must be explicitly set, but it can be void. Under --noImplicitVoidable, it would be a strong guarantee of existence.

x: ?string is new: it must be explicitly set, but can be void. As is, it behaves exactly like w, but provides more safety for a consumer.

y?: string continues to behave as-is. Under --noImplicitVoidable, though, it would probably be treated as "undefinable" - that is, it may have a value of undefined (implicitly, if it's not set) and accessing it must be guarded, but it would never have a value of null.

z?: ?string is equivalent to y, but again provides more safety for consumers and would be preferred over y in most new code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions