The Wayback Machine - https://web.archive.org/web/20250601141231/https://github.com/microsoft/TypeScript/pull/49146
Skip to content

Avoid binding readonly properties as special properties on a function symbol #49146

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

Andarist
Copy link
Contributor

fixes #49113

@typescript-bot typescript-bot added the For Backlog Bug PRs that fix a backlog bug label May 17, 2022
if (!isInJSFile(node) && !isFunctionSym) {
return;
}
if (isFunctionSym && getDeclarationName(cast(node.left, isBindableStaticNameExpression)) === "length") {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not super happy about this fix but binder doesn't have access any access to the type information so I can't just check what is declared on the interface Function as readonly.

And I think that it's better to avoid binding this property altogether than trying to account for this case in the checker or to write some fixup logic in between the binder and the checker.

I've originally described my conclusion after the debugging session here: #49113 (comment)


f0.length = 1;
>f0.length = 1 : 1
>f0.length : any
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels wrong - perhaps the .types printer tries to only lookup the "target" symbol of this assignment and doesn't try to look into Function here etc. Should I try to fix this as part of this PR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be fine. What the type writer is saying is that the type of the expression f0.length = 1 is 1, which is correct:

// note: NOT in strict mode. In strict mode, attempting to assign to length will throw
function f(a, b) { }
console.log(f.length); // 2

const i = f.length = 1;
console.log(i); // 1
console.log(f.length); // 2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was specifically referring to the 9th line that has f0.length : any and it seems that this any is a little bit off here as I would expect this to be of type number 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, but actually this is already the case for new Function case so perhaps this is how it's printed for the readonly LHS. It's still a little bit confusing to me - but at least my PR doesn't really change anything in this regard 🤷‍♂️

@Andarist Andarist force-pushed the fix/fn-readonly-length branch from b390e46 to 6046ffb Compare May 17, 2022 09:40
if (!isInJSFile(node) && !isFunctionSym) {
return;
}
if (isFunctionSym && isBindableStaticNameExpression(node.left) && getDeclarationName(node.left) === "length") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we would be able to query the type of Function ahead of time, but this is done in the binder. Is there some machinery to do this later in the checker? @rbuckton @ahejlsberg

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the ability to introduce new bindings in the checker for properties coming from literal types or unique symbol types, but not for this case.

This seems like an acceptable way to do this, since length is a special case per spec (since it is writable: false, configurable: true). We should probably also do this for name, since it has the same restrictions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, name was what I was thinking of. I just generally don't like these special-cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've extended the logic to handle name as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole concept of "special property assignments" is a special case. In for a penny...

@Andarist Andarist changed the title Avoid binding length property as special property on a function symbol Avoid binding readonly properties as special properties on a function symbol May 17, 2022
@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented May 18, 2022

I don't think the right fix is to modify the binder. My rough sketch of a fix here would be to, when synthesizing the property from the SPA, also include normal name resolution that can find apparent properties, and mix the flags of the two symbols correctly.

It's possible this isn't worth fixing given the very small number of times it comes up vs how often we'd have to do the sort of work implied by either fix.

@Andarist
Copy link
Contributor Author

What does synthesizing mean in this context? How I would recognize there that a property comes from SPA? How one can combine multiple name resolutions? I've thought that a property lookup is recursive but that it only checks a single "layer" at a time. Is there any such thing that you are describing anywhere in the codebase so I could take a look there to understand it better? Or would this be a new mechanism?

@Andarist
Copy link
Contributor Author

Do we have any consensus on how I should proceed here? What kind of a solution is preferred?

@sandersn
Copy link
Member

I think this is the right fix. It is a special-case, but it's one-based on the spec, similar to the weird way the two-pass binding of functions first to simulate lifting.
I do think it should cover all readonly properties of function (for functions), classes (if classes have any) and objects, based on the container's initialiser.

Ryan's solution doesn't work well because property-assignment declarations happen in the binder, which doesn't have the ability to look up apparent types. Once the property is bound, it's hard to distinguish from any other property; you'd have to special-case function, class and object types in the checker to prioritise apparent-type lookup of properties before own properties.

@Andarist this has been sitting a while. Do you want to revive this? It's not high priority, obviously, or we wouldn't have it let it sit for so long.

@Andarist
Copy link
Contributor Author

@Andarist this has been sitting a while. Do you want to revive this? It's not high priority, obviously, or we wouldn't have it let it sit for so long.

Yes, I can revive this soon-ish and ping you for re-review then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug
Projects
Status: Waiting on reviewers
Development

Successfully merging this pull request may close these issues.

.length property of function literals is not readonly
6 participants