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

Duplicate identifier in function declaration due to stripped property renames #50707

Closed
72636c opened this issue Sep 9, 2022 · 6 comments · Fixed by #50779
Closed

Duplicate identifier in function declaration due to stripped property renames #50707

72636c opened this issue Sep 9, 2022 · 6 comments · Fixed by #50779
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@72636c
Copy link

72636c commented Sep 9, 2022

Bug Report

🔎 Search Terms

TS2300

🕗 Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play?#code/MYewdgzgLgBAZmGBeGAKA3jADgJxFgLhgEMAabPQmAIxgF8jNd8iwBXAW2oFMd6BKZAD4SMANQ0A3EA

💻 Code

const fn = ({ prop: a, prop: b }: { prop: number }) => a + b;

Or alternatively:

const fn = ({ prop: a }: { prop: number }, { prop: b }: { prop: number }) => a + b;

🙁 Actual behavior

.D.TS on TypeScript 4.8.2:

// error TS2300: Duplicate identifier 'prop'.
declare const fn: ({ prop, prop }: {
    prop: number;
}) => number;

https://www.typescriptlang.org/play?ts=4.8.2#code/MYewdgzgLgBAZmGBeGAKA3jADgJxFgLhgEMAabPQmAIxgF8jNd8iwBXAW2oFMd6BKZAD4SMANQ0A3EA

🙂 Expected behavior

.D.TS on TypeScript 4.7.4:

declare const fn: ({ prop: a, prop: b }: {
    prop: number;
}) => number;

https://www.typescriptlang.org/play?ts=4.7.4#code/MYewdgzgLgBAZmGBeGAKA3jADgJxFgLhgEMAabPQmAIxgF8jNd8iwBXAW2oFMd6BKZAD4SMANQ0A3EA

@jakebailey jakebailey added the Bug A bug in TypeScript label Sep 12, 2022
@jakebailey
Copy link
Member

jakebailey commented Sep 12, 2022

#50537 fixed this by leaving the renames in if the name was a keyword, but now if the same property is destructured into two different names, the new names still have to be preserved again, too, to avoid an error.

At this point, I'm wondering why we are even emitting these into d.ts files at all... Wouldn't it make more sense to instead emit {}: { prop: number } (or in the other issue, {}: GetLocalesOptions<T>), or at least a made-up parameter name, as these names are an implementation detail of the function?

@weswigham @andrewbranch

@weswigham
Copy link
Member

weswigham commented Sep 12, 2022

At this point, I'm wondering why we are even emitting these into d.ts files at all...

Extra documentation is about the only reason. Autocomplete for callers is going to come from the type.

@andrewbranch
Copy link
Member

I would propose preserving duplicates in 4.8.4 and then I’d be fine with emitting {} in 4.9+, personally.

@jakebailey
Copy link
Member

jakebailey commented Sep 14, 2022

I've been looking into this; unfortunately it seems like the way that #41044 and #50537 operate make it prohibitively difficult to catch the the second case in the post:

const fn = ({ prop: a }: { prop: number }, { prop: b }: { prop: number }) => a + b;

The code that was modified is part of symbolToParameterDeclaration; that is called independently on individual parameters, so you can't really figure out when you're duplicating something, other than if you have a duplicate within the parameter itself (the first example in the post), unless you start sifting around peer parameters or something. Also, doing the elision there would remove these from from things like hover, which feels undesirable.

I think that we should just do a partial revert of #41044 (for both main and 4.8), removing the elision in cloneBindingName. That would restore the pre-4.8 behavior. Then for 4.9, I think that these can be dropped during d.ts emit in the declaration transform, the real place where we don't want these.

@jakebailey
Copy link
Member

I have a fix that does drop the binding patterns entirely, except it won't work because this is legal:

function fn({ x }: { x: string }, y: typeof x): typeof x {
    return x + y;
}

If we drop the binding pattern indiscriminately, we can't refer to x in from typeof in other parameters and the return type. I could go so far as to preserve elements that are used, maybe, but I'm sort of thinking that we should leave the status quo.

Also, it appears as though we error when we have a optional binding element parameter and the binding element is empty:

declare function fn1({}?: { x: string }): void; // error

declare function fn2({ x }?: { x: string }): void; // OK?

declare function fn3([]?: [ x: string ]): void; // error

declare function fn4([ x ]?: [ x: string ]): void; // OK?

Playground Link

This trips up at least one test case; this seems like a bug to me and I'll report it elsewhere.

@jakebailey
Copy link
Member

I've merged #50779 for now, which does fix this issue, but per the above I don't know if it's a good idea to do the empty binding element transformation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants