Skip to content

Mutating a Set can change the size - but lib.es2015.collection.d.ts declares size as a readonly propertyΒ #62468

@mstange

Description

@mstange

πŸ”Ž Search Terms

"Set", "size", "readonly", "mutate"

πŸ•— Version & Regression Information

  • I was unwilling to test this on prior versions

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABBOBbADiKBTAciVAI2wCcAKASgC5EwDiTEBvAKEXeQQGcpEvEAvLWwB3RAGVsUADx0ipAHxkA2gGYALAF0KAbjYcYwRGS4A6LjABe2QQKEBGCs30cOZgCbYANlOxkNui6uiIbGZhbWtg5OrMFxiCRSICRIAKx68YgAvkGuiVDJSOFWNgA+pYgARACGfFAkMGAA5pUZHDl5SSmI6Sw5LChgXHA+pl5wTWQoGFh49KSUFCxAA

πŸ’» Code

function computeNumber(): number {
    const s = new Set<number>([34]);
    if (s.size === 1) {
        s.delete(34);
        if (s.size === 1) {
            return 5;
        }
        return s.size || "a string";
    }
    return 5;
}

πŸ™ Actual behavior

The code in the snippet passes the type checker even though this function returns a string instead of a number.

The size property of Set is typed in such a way that TS thinks that it can never change on a set object.

πŸ™‚ Expected behavior

The snippet should fail to type check because it returns a string whereas the function type requires a number to be returned instead.

The size property of Set should be typed in such a way that TS knows that it can change when set member functions are called.

Additional information about the issue

It appears that lib.es2015.collection.d.ts types the size property in such a way that TypeScript thinks it will never change.

In my actual code this causes a false positive of @typescript-eslint/no-unnecessary-condition in code like this:

    if (newSelectedThreadIndexes.size === 0) {
      // call newSelectedThreadIndexes.add conditionally
      // [...]

      if (newSelectedThreadIndexes.size === 0) { // <-- "Unnecessary conditional, comparison is always true, since `0 === 0` is true"
        // No thread could be found, so do not isolate this process.
        return;
      }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions