Skip to content

Type-guard-assertion-like syntax does not do anything #5731

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

Closed
saschanaz opened this issue Nov 20, 2015 · 12 comments
Closed

Type-guard-assertion-like syntax does not do anything #5731

saschanaz opened this issue Nov 20, 2015 · 12 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@saschanaz
Copy link
Contributor

The following code does not cause any error nor does type-guard thing.

let foo: Node
if (<foo is Element>(foo.nodeType === 1)) { 
}

Is this by design? The language service implicitly thinks the foo inside type assertion as any. I would prefer to making this as a valid type guard syntax if there is no spec about this.

@ahejlsberg
Copy link
Member

That's a bug. The type predicate syntax should only be allowed in a return type position.

@ahejlsberg ahejlsberg added the Bug A bug in TypeScript label Nov 20, 2015
@saschanaz
Copy link
Contributor Author

@ahejlsberg Can we make it valid? It may allow inline type-guard which will be useful when the custom type guard function (1) only requires one single property check (2) is being used only once.

@weswigham
Copy link
Member

I'd like to point out that this is also true with as style casts:

let foo: Node
if ((foo.nodeType === 1) as foo is Element) {
}

@mhegazy mhegazy added this to the TypeScript 1.8 milestone Dec 2, 2015
@mhegazy
Copy link
Contributor

mhegazy commented Dec 2, 2015

@sandersn can you take a look

@sandersn
Copy link
Member

sandersn commented Dec 4, 2015

I'll disable this for now because I see one big open question with inline type predicates:

How is the variable that is part of the predicate related to the variables mentioned in the expression? For example, is this legal? If so, should any checking occur on the expression with respect to the predicate?

let x: number | string;
let y: number;
let num: number;
if(<x is number>(y > 12)) {
  num = x;
}

@saschanaz
Copy link
Contributor Author

@sandersn I think your example can be translated to this one, and if this is legal then that should be legal too.

let x: number|string;
let y: number;
let num: number;
if (isNumber(x,y)){
    num = x;
}

function isNumber(x: number|string, y: number): x is number {
    return y > 12;
}

@sandersn
Copy link
Member

sandersn commented Dec 7, 2015

I tried writing a proposal yesterday but hit a couple big problems. I was asking the wrong question about scope, but the problem is about scope. Here's the proposal. The variable capture question could be resolved by your proposed rewrite to function, but no other expression does that that I know of.

Type Predicate Expressions

A type predicate expression allows you to narrow a union type with a single expression. The syntax is

<variableistype>boolean-expression

When the expression is used in a conditional context, the true branch of the conditional narrows variable to type, while the false branch narrows variable by removing type.

The type predicate expression is of type Boolean, with apparent type of type predicate. The right hand side must be an expression of type Boolean. The left hand side's variable must be a name bound in the current scope. The left hand side's type must be a name bound in the current scope.

Open questions

  1. How does variable capture work? Can a type predicate expression be returned from a function and used to narrow a variable that's no longer in scope? This could be defined to cause an error, but that restriction is neither obvious nor easy to use.
  2. Why reuse the type assertion syntax? The type of the right expression is checked to be Boolean, unlike assertions, and the resulting type is still Boolean, also unlike assertions.

@saschanaz
Copy link
Contributor Author

  1. I think it should be explicitly on function return type.
function foo(x) {
    return isNumber(x); // This currently does not implicitly make `foo` a type predicate
}
function isNumber(x: string|number): x is number {
    return typeof x === "number";
}

@sandersn
Copy link
Member

sandersn commented Dec 9, 2015

Here's an example of what I'm thinking about with (1):

function foo() {
  const x: number | string = 12;
  return <x is number>(typeof x === "number");
}
if(foo()) {
  // in this block, x is number. What does that even mean?
}

@saschanaz
Copy link
Contributor Author

I think returning type predicate expressions should just make them normal Booleans. It will make them consistent with current type predicates.

function foo() {
    const x: number | string = 12;
    return isNumber(x); // this makes the return type of `foo` just boolean

    function isNumber(x) {
        return typeof x === "number";
    }
}
if (foo()) {
    // nothing special happens
}
const x: number | string = 12;
function foo() {
   let xIsNumber = <x is number>(typeof x === "number");
   if (xIsNumber) {
       // x is number
   }
   return xIsNumber; // Boolean!
}
if (foo()) {
    // still nothing happens
}
const x: number | string = 12;
function foo() {
   return <x is number>(typeof x === "number"); // Maybe future TSLint: this does nothing!
}

@saschanaz
Copy link
Contributor Author

Not sure about alternative syntax. One in my thought:

boolean-expression implying variable is type

@sandersn
Copy link
Member

Fixed by #5992.

@mhegazy mhegazy added the Fixed A PR has been merged for this issue label Jan 13, 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

5 participants