Skip to content
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

type nothing emitted in generated .d.ts files #8345

Closed
mhegazy opened this issue Apr 28, 2016 · 20 comments
Closed

type nothing emitted in generated .d.ts files #8345

mhegazy opened this issue Apr 28, 2016 · 20 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@mhegazy
Copy link
Contributor

mhegazy commented Apr 28, 2016

function f() {
    var x: string | undefined = undefined;
    y = 0;
    if (x) {
       var y: typeof x;
    }
    return y; // y has type nothing
}

generates invalid .d.ts file

declare function f(): nothing;

this is a regression introduced by #8340

@mhegazy mhegazy added the Bug A bug in TypeScript label Apr 28, 2016
@ahejlsberg
Copy link
Member

ahejlsberg commented Apr 28, 2016

I'm tempted to say just don't do that. But I won't. 😉

I definitely don't think the solution is to make nothing a manifest type that you can reference in a type annotation. That leaves rewriting (but to what?) or issuing an error. The error is probably the better option.

@DanielRosenwasser
Copy link
Member

I get why {} was not appropriate. But nothing sounds a heck of a lot like what I'd associate void with, so what exactly is the intended difference semantically?

@mihailik
Copy link
Contributor

Can it return any and for those interested in picking at those little tiny details let noImplicitAny generate an error?

@ahejlsberg
Copy link
Member

@DanielRosenwasser There are a couple of important differences between void and nothing.

  • The undefined type is assignable to void, but no types are assignable to nothing.
  • void can be a constituent of a union type, but nothing disappears in a union type (because it signifies no type at all).

I do agree we have bit of a profusion of nothingness types (undefined, null, void, nothing), but they all have important differences. If anything had to change, we could possibly consider removing the undefined type and having void be the type of the undefined literal. But that would allow silly stuff like assigning the result of a void function to a variable can be undefined, which really should be an error.

@mhegazy
Copy link
Contributor Author

mhegazy commented Apr 28, 2016

here is a less artificial example:

function bar() {
    var x: string | undefined;
    if (x) {
        return x;
    }
    throw new Error();
}

@mhegazy
Copy link
Contributor Author

mhegazy commented Apr 28, 2016

so for .d.ts purposes, should we 1. write void, 2. undefined, 3. nothing and make it legal to name this type.

@ahejlsberg
Copy link
Member

(1) and (2) aren't correct and (3) really doesn't help anyone. I think we should (4) issue an error.

@ahejlsberg
Copy link
Member

Adding some rationale for (4): There's really no situation ever where you want nothing to be the declared type of an entity. It is typically an indicator of a logic error, but we don't want to eagerly issue the error because you may be writing "defensive" code that deals with incorrect parameters passed from code that doesn't follow the stated contract (such as plain JavaScript).

@mihailik
Copy link
Contributor

function bar() {
    throw new Error();
}

Under these reasoning this should produce error too.

@mhegazy
Copy link
Contributor Author

mhegazy commented Apr 28, 2016

no that is void.

@mihailik
Copy link
Contributor

mihailik commented Apr 28, 2016

Exactly, and that's the same code as in your example (sans no-op if).

Do we end-users of the compiler need this level of precision? By all means it may add value internally, but best reduce peculiarity on the outside, coercing nothing to void or unknown in codegen/diagnostic messages.

@mhegazy
Copy link
Contributor Author

mhegazy commented Apr 28, 2016

void means it returns no value. nothing means it returned a value, but its type was narrowed down by the compiler to nothing. The assumption is that is never the intention of the user. so if you did not mean for this to happen, so you might want to take a look at the source and see where did the nothing came from, and the error does that.

@aluanhaddad
Copy link
Contributor

Do we end-users of the compiler need this level of precision? By all means it may add value internally, but best reduce peculiarity on the outside, coercing nothing to void or unknown in codegen/diagnostic messages.

The TypeScript compiler is a TypeScript program so I do not think that would be either possible or valuable.

👍 for making it an error.

@mihailik
Copy link
Contributor

mihailik commented Apr 29, 2016

@mhegazy unless this detection generates false positives, as @ahejlsberg pointed out above.

function validateType(val: number | string) {
    if (typeof val === 'string') return null;
    else {
        if (typeof val === 'number') return null;
        else return val; // validation fails at runtime, but this code is valid
    }
}

function validateTypeMoreDetails(val: number | string) {
    if (typeof val === 'string') return null;
    else {
        if (typeof val === 'number') return null;
        else return 'Incorrect type ' + typeof val+' ('+val+')'; // should 'nothing' support plus operator with string?
    }
}

@mihailik
Copy link
Contributor

Set theory (empowered by union and intersection types) is bound to produce oddities -- hence a need to limit that power in places where it becomes too exotic and impractical.

Coercing nothing to void or similar may be one of those intentional trivializations for the sake of sanity and practicality. You know saying 'the buck stops with me'? Make that nothing buck stay within the compiler, don't pass it out in the world.

@DanielRosenwasser
Copy link
Member

I don't think the nothing type is actually that big of a deal, I just think it's odd to make it an inexpressible type. I don't see a reason to limit users from writing it out like with how null and undefined use to be disallowed.

@jeffreymorlan
Copy link
Contributor

There's really no situation ever where you want nothing to be the declared type of an entity.

A function that always throws an exception, never returning normally, logically has a return value of nothing. Unlike a void-returning function, a nothing-returning function could safely be assigned to any other return type:

function stub() { throw Error("Unimplemented"); }
interface SomeInterface {
    someMethod(): number;
    someIrrelevantMethod1(): number;
    someIrrelevantMethod2(): string;
}
var obj: SomeInterface = {
    someMethod() { ... },
    someIrrelevantMethod1: stub,
    someIrrelevantMethod2: stub
};

Currently this is an error, despite being perfectly type-safe - making stub return nothing would fix that. (Or any, but that makes the type less self-documenting)

@aluanhaddad
Copy link
Contributor

@jeffreymorlan perfectly typesafe, but known to produce an error when invoked.

@mhegazy
Copy link
Contributor Author

mhegazy commented May 17, 2016

should be fixed by #8652

@mhegazy mhegazy assigned ghost and ahejlsberg and unassigned ghost May 17, 2016
@mhegazy mhegazy added this to the TypeScript 2.0 milestone May 17, 2016
@ahejlsberg
Copy link
Member

Fixed in #8652.

@mhegazy mhegazy added the Fixed A PR has been merged for this issue label May 18, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

6 participants