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

simulate scope at let to a if statement but failed #36853

Closed
arex0 opened this issue Feb 18, 2020 · 5 comments
Closed

simulate scope at let to a if statement but failed #36853

arex0 opened this issue Feb 18, 2020 · 5 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@arex0
Copy link

arex0 commented Feb 18, 2020

TypeScript Version: 3.7.5

Search Terms: if-let, scope, closure

Expected behavior: tsc report no error

Actual behavior: tsc report "Variable 'a' is used before being assigned."

Related Issues:

Code
if-let statement is easy to control scope in golang

var a A 
if i := bytes.Index(s,sep); i != -1 {
  a = makeA(i) // use i here
}else{
  a = make(A)
  // others code do not use i here
}
a.doSomthing()

I try to use closure to simulate it, because ts can not use if-let statement. tsc reported error "Variable 'a' is used before being assigned."

class A {
    i: number | undefined
    constructor(i?: number) {
        if (i !== undefined) {
            this.i = i
        }
    }
    doSomething() {
        if (this.i === undefined) console.log("A init in else statement")
        else console.log("A init in if statement")
    }
}

function f(sep: number) {
    let a: A
    if (((i: number) => {
        if (i == -1) return false
        a = new A(i) // use i there
        return true
    })(arr.indexOf(sep))) {
    } else {
        a = new A
        // others code do not use i there
    }
    a.doSomething()
}

let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

f(10)
f(5)
Output
"use strict";
class A {
    constructor(i) {
        if (i !== undefined) {
            this.i = i;
        }
    }
    doSomething() {
        if (this.i === undefined)
            console.log("A init in else statement");
        else
            console.log("A init in if statement");
    }
}
function f(sep) {
    let a;
    if (((i) => {
        if (i == -1)
            return false;
        a = new A(i); // use i there
        return true;
    })(arr.indexOf(sep))) {
    }
    else {
        a = new A;
        // others code do not use i there
    }
    a.doSomething();
}
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
f(10);
f(5);
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "useDefineForClassFields": false,
    "alwaysStrict": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "downlevelIteration": false,
    "noEmitHelpers": false,
    "noLib": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "esModuleInterop": true,
    "preserveConstEnums": false,
    "removeComments": false,
    "skipLibCheck": false,
    "checkJs": false,
    "allowJs": false,
    "declaration": true,
    "experimentalDecorators": false,
    "emitDecoratorMetadata": false,
    "target": "ES2017",
    "module": "ESNext"
  }
}

Playground Link: Provided

@jcalz
Copy link
Contributor

jcalz commented Feb 18, 2020

The new issue template asks for a Playground link https://www.typescriptlang.org/play/ where your issue is reproduced. The above code has several errors due to missing declarations, but not the one you're reporting. Please consider cleaning it up if you don't want this closed as unactionable. Good luck!

@arex0
Copy link
Author

arex0 commented Feb 18, 2020

@jcalz thank you for you commented, it is clean up now

@MartinJohns
Copy link
Contributor

Duplicate of #9998.

@jcalz
Copy link
Contributor

jcalz commented Feb 18, 2020

I don't know about #9998; the function here is an IIFE which, IIRC (ha ha), is inlined:

let z: string;
(() => { z = "a" })()
z.toUpperCase(); // okay

Instead I think it has to do with trying to "save" the results of CFA in variables, return them from functions, etc., and then use them later... which doesn't work, as in #12184, #20497, #24865 (and probably others):

let a: string;
const bool = Math.random() < 0.5;
if (bool) { a = ""; }
if (!bool) { a = ""; }
a.toUpperCase(); // error

In any case, a mcve for this probably looks like:

let b: string;
if ((() => {
    if (Math.random() < 0.5) return true;
    b = "";
    return false;
})()) {
    b = "";
}
b.toUpperCase(); // error

Playground link

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Feb 19, 2020
@RyanCavanaugh
Copy link
Member

We don't have any kind of mechanisms capable of creating inferences like "This function either returns true and makes an assignment, or returns false when it doesn't", nor frequently-encountered use cases in which case this kind of reasoning is necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

4 participants