Skip to content

Unexpected circularity error when variable is initialized from a class member #61606

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
Gr3q opened this issue Apr 22, 2025 · 3 comments · May be fixed by #61608
Open

Unexpected circularity error when variable is initialized from a class member #61606

Gr3q opened this issue Apr 22, 2025 · 3 comments · May be fixed by #61608
Labels
Bug A bug in TypeScript Help Wanted You can do this
Milestone

Comments

@Gr3q
Copy link

Gr3q commented Apr 22, 2025

🔎 Search Terms

loss of type, for loop, for-loop, class field

🕗 Version & Regression Information

  • This changed between versions ______ and _______
  • This changed in commit or PR _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about _________
  • I was unable to test this on prior versions because _______

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/JYOwLgpgTgZghgYwgAgIIAd0BtgLmYAexGQG8AoZK5YAEwC5kBnMKUAcwG5zLq9J2hKAE9GFahJoNmrDtwkBfckvIIscJkzSYc-IiC3jq6AK4AjXcgBioWhmy58+gBQBHE9FEy2IdgEpGe10nYmQAH2QQEywsMl5JZCgIMBMoEiiY+WolFTUNLQAVCBY4iQBlCCwIBEg7HUcCYiZGCrAAHhYfdgA+ZABeSIgAd2RW5z8sqiCG-WbtBz0m8Mjo2IGMrEnkdmTRyura6cWDACFhAGF8CEERccYABTgoAjgsNoAlaqFaDtlfABo9u1OhxumDlhtSglkAB6GHIADqQgA1k9CCYQLRkABGRgAW0IADcUGAABbALQWQgIZE0Ax0FAwITILCEQjoeKSOGIlFojFYgBMLTgxOQAAMyRSAHRHEIGMXIMCEZBwZAIJpgRWkiAkExMEmk-CciTAGDIZySpgy+rHLR9e0rGJ+KHQ6hJFJpR2bY3ZHiu9UGTVJJjRMAPJ4vN6fdVQH4ggFA35dMG9AakBTcH1UJlQc0BkpwG1ygCSABFkIQzZapRUqjUIHUFnKmM6jK6qPnNYWm40SANq7Le1abJjBy5u8Fe2WJlnJKbzROZqF7etVq3Z-7iAQohAtq6VO2+Bq1VcbsIy-0VUXe1L+NchMIpXQ99DO1ee7NL8HQwBtO9nssAF1kAAfhAwYRlaJNQXGF8EkXW0pTgWhaGcBC5SfWgZ0PKhuXOQg8TxHUwBhDF1UI4itUZQgYkIIYOBZUASWVfUUGATUzCSOBkVIkBOIgbiNzdYpf3-B8gMvdChzg5AD2hd1UhIb8sDALYciAA

💻 Code

interface Application {
    id: string;

    category: {
        id: string;
    }
}

class Applications {
    public FindApplication(query: string): Application | null {
        return null;
    }
}

class Test {
    SelectedApplications: Set<string> = new Set();
    Applications: Applications | null = null;
    get SelectedApplicationsByCategory(): Partial<Record<string, Set<string>>> | null {
        // Workaround 1: move this block inside for loop
        // Workaround 2: Save this.Applications to a const then use that
        if (this.Applications === null) {
            return null;
        }

        const result: Partial<Record<string, Set<string>>> = {};

        for (const applicationID of this.SelectedApplications) {
            const application = this.Applications.FindApplication(applicationID);
            if (application === null) {
                continue;
            }

            const categoryID = application.category.id;
            const applications = result[categoryID] ?? new Set<string>();
            applications.add(application.id);
            // Comment/uncomment the following line to see it break/unbreak
            result[categoryID] = applications;
        }

        return result;
    }
}

🙁 Actual behavior

application has type: any. FindApplication returns Application | null, but that gets lost somewhere.

🙂 Expected behavior

type for application should be Application | null.

Additional information about the issue

See comments in code for workarounds and to see another line break type inference.

@Gr3q Gr3q changed the title Loss of type for class field in for loop Loss of type in for-of loop Apr 22, 2025
@RyanCavanaugh
Copy link
Member

Smallest repro I could find. Very weird.

interface Application {
    id: number;
}

const result: boolean[] = [];

class Test {
    appArray: Application[] | null = null;

    foo(): void {
        if (this.appArray === null) {
            return;
        }

        for (const idx of [0, 1]) {
            const app = this.appArray[idx];
            const categoryID = app.id;
            result[categoryID] = true;
        }
    }
}

@RyanCavanaugh RyanCavanaugh changed the title Loss of type in for-of loop Unexpected circularity error when variable is initialized from a class member Apr 22, 2025
@RyanCavanaugh
Copy link
Member

Bisects to #45974

@RyanCavanaugh
Copy link
Member

Shorter, looking at the PR

const result: boolean[] = [];
class Test {
    appArray: number[] | null = null;
    foo(): void {
        if (this.appArray === null) {
            return;
        }
        for (let i = 0; i < 1; i++) {
            const app = this.appArray[0];
            const appRef = app;
            result[appRef] = true;
        }
    }
}

@RyanCavanaugh RyanCavanaugh added Bug A bug in TypeScript Help Wanted You can do this labels Apr 22, 2025
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Apr 22, 2025
RyanCavanaugh added a commit to RyanCavanaugh/TypeScript that referenced this issue Apr 22, 2025
…erty key equivalence

Fixes microsoft#61606. Need PR results to see if this is worth it or not.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Bug A bug in TypeScript Help Wanted You can do this
Projects
None yet
2 participants