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

Binding element alias is kept even though it is unused in declaration files #55654

Closed
dragomirtitian opened this issue Sep 6, 2023 · 15 comments Β· Fixed by #55683
Closed

Binding element alias is kept even though it is unused in declaration files #55654

dragomirtitian opened this issue Sep 6, 2023 · 15 comments Β· Fixed by #55683
Labels
Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases
Milestone

Comments

@dragomirtitian
Copy link
Contributor

dragomirtitian commented Sep 6, 2023

πŸ”Ž Search Terms

binding element alias declaration files

πŸ•— Version & Regression Information

  • This changed between versions 4.7 and 4.8

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.2.2#code/PTAEhRyUFgCgVAygCwPYFcA2ATUA7FALqAEYCmoA1qQA5ECGuOAlgM6gBOpAtigG6lZYAMzS4AxgSYpcoFqkxYAcoQDSpGqAAUAbzx0upAFyg6GJnTYBfY7tz6jsguya4A5qEsBKUNtih-0DCWsLDwgDLkoWAAgmYWoKygaCwC8TIESOQi4pLSJGhE+EQuoOnkLEyudgRonJGI8th4hCTkVLSpoFikYhh07HQ5uGxxTEXDMkxc1BjcpLgEA1IyXQtMGHUsAJ5cxCgYAHSsAEqkQqSc4ikJSSkA7khMYkigT90UbJxnF2Kk45v351I+2EogkS1kDSUqnU1AAQvkAJIsNTtHR6AzGUzmKw2dEOFhOFzuLy+GABDikarsGRYiywYIwOrgOoAVWSzBknCpaU21HImjE6EafNA1D69gI5zYBF5vxMbFupAwGG8LBQ8SIciFODIlBoBBB2XBWrQWAwag0aLsGJMsRxPjxxgJzjcHk8xhlfJQQlt2J8fgCXJqNLt9KAA

πŸ’» Code

// βœ” 
// Should not be kept and is removed
function shouldNotKeep ({ name: alias }: { name: string }) {
    
}

// ❌
// Alias is used in the function but not in the signature
// Should not be kept in declarations as it is an implementation detail
// symbol.isReferenced is used which checks references anywhere.
function shouldNotKeepButIsKept ({ name: alias }: { name: string }){
    return alias
}

// βœ”
// Used in return type (could pe parameters types as well) so it should be kept
function shoudlKeep ({ name: alias }: { name: string }): typeof alias {
    return alias
}

πŸ™ Actual behavior

alias is kept in declaration files for shouldNotKeepButIsKept

πŸ™‚ Expected behavior

Either alias is removed from declaration file for shouldNotKeepButIsKept, or alias is also kept for shouldNotKeep.

Additional information about the issue

Seems like declaration emit is using isReferenced of the symbol. But this tracks usage of the symbol anywhere in the scope, we would need usage of the symbol in the function signature.

@jakebailey

This comment was marked as outdated.

@jakebailey
Copy link
Member

Bisects to #41044.

@andrewbranch
Copy link
Member

I would have thought this was more a consequence of #50779

@jakebailey
Copy link
Member

Me too! (I couldn't remember which PR that was)

@andrewbranch
Copy link
Member

Is this a problem? Can’t isolatedDeclarations always keep them?

@jakebailey
Copy link
Member

Probably; I don't think there's much harm besides including an implementation detail, which is the main reason we were trying to drop them out of d.ts files. The main reason to keep them is typeof, which isolatedDeclarations won't be emitting, so it's sorta funky to not remove them.

In #50779 I seemed to think I was going to send a follow-up to improve things but clearly I never did, but the fact that this was broken by #41044 and not #50779 is very interesting.

(Also makes me think about #49627...)

@jakebailey
Copy link
Member

jakebailey commented Sep 8, 2023

I've sent a "fix" at #55678 which just continues down the the path of reverting #41044; realistically in #50779 I should have fully undone the renaming removals but I don't think I noticed the additional code path doing the same thing in declaration emit. I bet there's some test case that would break in a similar manner to what was fixed in #50779 because of this problem.

I don't know if this is ideal; obviously it'd be better to not emit renamings at all if we don't need them, but I'm not totally sure if we have the machinery in place to query "will I need this name somewhere else eventually in the emit".

@jakebailey
Copy link
Member

Of course, an alternative is to just "do nothing" and accept that this is a limitation of the declaration emit, and that adding it when it's unused is harmless.

But as I said in #55678, if you're destructuring a parameter in your .ts files, you're probably using the parameter, so you're probably going to have it marked as used anyway, meaning you're going to get it no matter what we do without going all-in on a new reference analysis.

@dragomirtitian
Copy link
Contributor Author

dragomirtitian commented Sep 8, 2023

The main reason to keep them is typeof, which isolatedDeclarations won't be emitting,

@jakebailey I mean it could emit typeof if it's specifically written in source. Not sure if the type printer would ever use typeof with such a symbol.

@jakebailey
Copy link
Member

Er, I thought we effectively found that typeof just can't be emitted with isolatedDeclarations?

@dragomirtitian
Copy link
Contributor Author

dragomirtitian commented Sep 8, 2023

@jakebailey If it written by the user, it will be copied like any other type. Isolated declaration will never forbit an explicit annotation. It just won't try to synthesize it. Performing the usage analysis to determine what needs to be kept to make the typeof valid is doable in isolated declarations and needs to be done anyway for other things, like imports.

I was looking at the code, and I think most of the necessary machinery to perform correct analysis is already there. I opened #55683 as a suggested fix.

@jakebailey
Copy link
Member

needs to be done anyway for other things, like imports.

Yeah, this is what I suspected.

I'm not convinced typeof is generally safe, though; e.g. in Playground Link the TS code on the left says you can't assign anything but 0 to the prop, but the declaration file would let you assign any number. Without type checking you can't figure that out.

@dragomirtitian
Copy link
Contributor Author

This is true, but the declarations emitted by TypeScript at present suffer from the same issue. The currently TSC generated declarations are:

export declare const x: number;
export declare const y: {
    x: typeof x;
};

Which don't account for the narrowing of x, just like a DTE would not.

If TSC decides to used the narrowed type at a later date, we can make the code above an isolated declaration error.

There will continue to be cases when either the code contains semantic errors or the code contains isolated declaration errors that are not detectable in a DTE without type info (few hopefully) that would mean a DTE successfully generates declarations that TSC would refuse to generate. This would be such a case.

@fatcerberus
Copy link

fatcerberus commented Sep 8, 2023

I guess I just don't understand why destructured parameters have to be in the .d.ts at all? Parameter destructuring is an implementation detail; any permutation of foo({ a }: { a: string }), aliased or otherwise, should be entirely equivalent from the caller's POV to foo(obj: { a: string }) so I don't get why TS doesn't just don't emit the latter in all cases, avoiding this issue entirely.

@jakebailey
Copy link
Member

jakebailey commented Sep 8, 2023

Scoping rules mean that typeof can operate on parameters inside of other parameter declarations or the return type, so they must be preserved if they are used. Before, we kept them always. Right now, we keep them if they are referenced (where referenced means anywhere, even the function body), and with the newer linked PR, it actually does the analysis in declaration emit.

@RyanCavanaugh RyanCavanaugh added Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases labels Sep 13, 2023
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Sep 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases
Projects
None yet
5 participants