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

if statement in loop changes type of variable outside the if #19955

Closed
ghost opened this issue Nov 13, 2017 · 8 comments
Closed

if statement in loop changes type of variable outside the if #19955

ghost opened this issue Nov 13, 2017 · 8 comments
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Milestone

Comments

@ghost
Copy link

ghost commented Nov 13, 2017

TypeScript Version: 2.7.0-dev.20171112

Code

type U = { kind: "A", xs?: number[] } | { kind: "B", xs?: number[] };
function f(u: U) {
	if (u.xs) {
		for (const b of u.xs) {
			if (u.kind === "A") {}
		}
	}
}

Expected behavior:

No error.

Actual behavior:

src/a.ts(4,19): error TS2532: Object is possibly 'undefined'.

@ghost ghost added the Bug A bug in TypeScript label Nov 13, 2017
@mhegazy mhegazy added this to the TypeScript 2.7 milestone Nov 13, 2017
@ahejlsberg
Copy link
Member

Duplicate of #18840.

@ahejlsberg ahejlsberg added Duplicate An existing issue was already created and removed Bug A bug in TypeScript labels Nov 13, 2017
@ahejlsberg
Copy link
Member

Actually, I'll flip it back to a bug since it is not exactly the same as #18840. However the root cause is similar: A narrowing by a discriminant property (such as u.kind === "A") resets any previous narrowing in effect for the variable. Here, the back edge of the for loop causes the narrowing of u.xs to disappear. This is unfortunately not a simple thing to fix.

@ahejlsberg ahejlsberg added Bug A bug in TypeScript and removed Duplicate An existing issue was already created labels Nov 13, 2017
@ghost
Copy link
Author

ghost commented Nov 14, 2017

Another case:

function containsItself(arr: {}): boolean {
    if (!Array.isArray(arr)) {
        return false;
    }
    for (const x of arr) {
        if (x === arr) {
            return true;
        }
    }
    return false;
}
src/a.ts(5,16): error TS7022: 'x' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

And another:

function f(u: { type: "A"; x: number; } | { type: "B"; x?: number; }) {
    if (u.type === "A") {
        u.x; //number
    }
    if (u.type === "B" && u.x !== undefined) {
        u.x; //number
    }
    if (u.type === "A" || u.type === "B" && u.x !== undefined) {
        u.x; // number | undefined
    }
}

@ghost
Copy link
Author

ghost commented Apr 10, 2018

A possibly related error:

interface Base { a: number | string; }
interface A extends Base { kind: "A"; }
interface B extends Base { kind: "B"; }

function f(x: A | B): void {
	if (typeof x.a === "number") {
		if (x.kind === "A") { } // No error if this is removed
		const n: number = x.a; // Error
	}
}

@ghost
Copy link
Author

ghost commented May 30, 2018

Another case:

function f(a: number | undefined) {
    if (!a) return;
    for (const b of [a]) {
        if (b === a) {}
    }
}
src/a.ts:3:16 - error TS7022: 'b' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

3     for (const b of [a]) {
                 ~

@mhegazy mhegazy modified the milestones: TypeScript 3.0, Future Jul 2, 2018
@ghost
Copy link
Author

ghost commented Aug 26, 2018

Another case from #26673:

interface A {
	isA: true;
	m?(): void;
}

interface B {
	isA: false;
	m?(): void;
}

class User {
	p!: A | B;

	go(): void {
		if (this.p.m) {
			if (this.p.isA) {}
			this.p.m(); // No error (good)
		}

		const p = this.p;
		if (p.m) {
			if (p.isA) {}
			p.m(); // Error (bad)
		}
	}
}

@jack-williams
Copy link
Collaborator

jack-williams commented Sep 24, 2019

This looks to be fixed by #32695; specifically, the cases involving loops.

@sandersn
Copy link
Member

sandersn commented Oct 1, 2019

Indeed it is. Thanks for noticing!

@sandersn sandersn closed this as completed Oct 1, 2019
@jack-williams jack-williams added the Fixed A PR has been merged for this issue label Oct 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

No branches or pull requests

4 participants