镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

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

Description

@Gr3q

🔎 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.

Activity

  1. changed the title [-]Loss of type for class field in for loop[/-] [+]Loss of type in for-of loop[/+] on Apr 22, 2025
  2. RyanCavanaugh commented on Apr 22, 2025

    @RyanCavanaugh
    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;
            }
        }
    }
  3. changed the title [-]Loss of type in for-of loop[/-] [+]Unexpected circularity error when variable is initialized from a class member[/+] on Apr 22, 2025
  4. RyanCavanaugh commented on Apr 22, 2025

    @RyanCavanaugh
    Member

    Bisects to #45974

  5. RyanCavanaugh commented on Apr 22, 2025

    @RyanCavanaugh
    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;
            }
        }
    }
  6. added a commit that references this issue on Apr 22, 2025
    366d3f4
  7. carlosalmonte04 commented on Jul 7, 2025

    @carlosalmonte04

    Another workaround is to add the type annotation.

    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: number = this.appArray[0]; // <- like this
                const appRef = app;
                result[appRef] = true;
            }
        }
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptDomain: check: Type Circularitystackoverflows caused by circularities, or unexpected errors where no circularity appears to existHelp WantedYou can do this

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions