You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Refactoring to convert to "named parameters" #23552
Apologies for being ungrateful: this doesn’t reflect how I use this pattern – I don’t see the parameters as a whole, I see each parameter individually. As a work-around, I inline the type and don’t define it externally.
Let me try to convince you, one last time (then I’ll stop pestering you), of the usefulness of a better notation for destructuring (nested destructuring will profit, too!). The following is an extreme example, but there are many similar functions in my code (“Showoff” is the name of my slide framework):
Note how, in the last two examples, you actually see parameters with names (vs. a single type for all parameters). The first example looks messy, the last two examples don’t.
If you have ever used a programming language with named parameters and liked them there – isn’t this a compelling use case? Given the thumbs-up at a recent comment of mine, there are quite a few people who agree.
This is the only aspect of my plain JavaScript code that became less usable after I moved to TypeScript.
Reacted by Brenton Simpson, Adam Black, Nils Hartmann, Tomasz Cudok, Ben Chauvette, Tirtha Guha, Illia Panasenko, Braulio Diez, Massimo Artizzu, Alan Plum and 81 more
Reacted by Tyler Sebastian, zenmumbler and AnyhowStep
Reacted by Glenn Dwiyatcita
Reacted by Sergey Bolshchikov, Roy Tinker, Emilio, Jon Rimmer, Kain Haart, Jeremiah Hall and Johan Lindstrom
I think there's a bit of a cargo-cult bias in the TS community to always prefer interfaces (because they can be extended). Type literals seem like a more appropriate model for the kwargs pattern, because they allow users to treat each named argument individually.
If a third party made a "convert to named arguments" command, I would probably find it useful to be able to write function signatures normally and then convert them to an interface/type literal. However, I'd rather the language supported setting the type for a named arg adjacent to the declaration of its name and default value. It's both hard to read and cumbersome to maintain when the types are separated from the rest of the definition.
Reacted by Axel Rauschmayer, Tomasz Cudok, Michał Bąkowski, Alan Plum, Corentin, Dominik Brack, Juergen Zimmermann, Venkat, Roy Tinker, Tom and 7 more
This refactor also must refactor all references to the method in the whole project right ? But a very helpful refactor , in my experience, happened many times when an API signature that needs to be backwards compatible, is defined with multiple params and then you keep adding parameters to implement new features or even change parameter type to OR, like existing: PreviousType|NewSemanticType....
Reacted by Tom, Andrii Dieiev, ExE Boss and Ian Yates
So I was playing a lot lately with Language Service APIs and friends and I have several refactors more or less working fine. This is the one suggested here (I think) :
And now I'm playing with some IPC communication between plugins in tsserver and host editor plugins /
extensions / packages that provide generic input-related operations - so I can inquire user for data in my refactors visually and keep being editor / IDE agnostic. For example, if I want to move a method to another class, or to another file I need to ask the user the destination and for that plugins can talk with these "Input Providers" generically. Since I'm manipulating the SourceFiles myself (not using FileEditRange ) I can do it synchronously. Really enjoying it will update you when I have something pretty to show.
This would be super helpful to me, even if others would rather a new destructuring syntax for function parameters.
So, if the proposed refactoring somehow loses favour over new destructuring syntax, I would still like for this particular one to be implemented.
I personally use interfaces instead of inlining object parameter types, especially for large projects. And projects I write that get consumed by others.
I've had to wrap a function many times. And if the library doesn't have those function arguments as an interface, then I have to copy-paste their inlined code instead of just using an interface that should already be there.
People like to think of interfaces as being "code duplication". But in the big picture, not having those interfaces causes code duplication.
So, in my personal opinion, inlining = save time now, but cause code duplication in the future/for downstream users of your library. Interface = a little extra time now, but reduce code duplication and make it easier for downstream users to extend/reuse/wrap code.
Have you thought about the default arguments with no types? Would you infer types there?
Good test case! Should "fall out" from the naive implementation.
Also would it work in constructor function with private or public keyword? I think it won't, right?
It probably should not for now.
where would you generate a type name if function is anonymous?
As a first-pass, it's probably reasonable to say that this would only generate a named type for
Function declarations
Single-variable declarations initialized with some sort of function expression/arrow function
Constructors without parameter properties
Method declarations
We could always come back to this and add it for signatures like call type literals, construct type literals, call/construct signatures, and method signatures (i.e. the ambient stuff).
Reacted by Ryan Cavanaugh, Michał Gołębiowski-Owczarek, Vincent, Gabriel and Daniel Gempesaw
Reacted by Ryan Cavanaugh, Holger Jeromin, Tobias Cohen, Anthony Ciccarello, Tobias Koppers, Jason Williams and Petr Homoky
Reacted by Ryan Cavanaugh, Gulshan, AnyhowStep, Bazyli Brzóska, David Sherret, Lucas Santos, Matija Grcic, Richard Gibson, Trotyl Yu, Oliver Joseph Ash and 3 more
I know Axel Rauschmayer (@rauschma) said he'd stop and seems to have kept his word, but he's absolutely right that doesn't express what people want to express.
Have people discussed using is yet? Example from the top using this syntax follows:
functionfoo({ais number,bis string,c? is boolean}): void{a;b;c}
And the TypeScript code base itself is chock full of long lists of parameters with optionals etc. Empirical evidence for where this could help.
Reacted by Axel Rauschmayer, Jon Rimmer, Torsten Stüber, Daniel Gempesaw, Jonathan Fleckenstein, Istora Mandiri and Johan Lindstrom
Have people discussed using is yet? Example from the top using this syntax follows:
functionfoo({ais number,bis string,c? is boolean}): void{a;b;c}
There were some discussions about as in an earlier issue -- mostly involving concerns about introducing a new keyword in a very specific context. I suspect is would have the same complaints.
I haven't thought through every issue, but I'm wondering about using : twice, where the renamed variable name can be optional. So instead of [object_key]: [variable_name] = [default_value] you end up with [object_key]: [variable_name]: [type] = [default_value]. This kind of makes sense since you're typing the variable name, rather than the object key, anyway.
So the most verbose version of this would be:
functionsomeFunc({foo: bar: string='default'}): void{bar;}
I wish we wouldn't encourage the pattern of making functions take a single object - it thwarts currying.
There are cases where it is beneficial to take a single object with many parameters.
For example, that object can be an immutable data structure and the function is just performing some operation on it and returning a new instance of the data structure.
In some cases, those data structures can have dozens of properties... You're not really suggesting we curry in those cases as well, right?
I love this refactoring, but is there a a reason why it does not work on constructors?
I sometimes find myself creating a class where the number of constructor parameters grows out of control, with too many optional ones and I wished that I had instead made one "config" or "options" interface that can be passed in to the constructor. The advantages being:
the constructor calls would be clearer, with objects passed in which explicitly have {key:value, key:value} rather than just a bunch of unnamed values passed in as constructor parameters, and
it makes it easier/cleaner to deal with a lot of optional parameters, since they can simply be omitted from the configuration object instead of all of the constructor parameters needing to be passed in the correct order (avoiding the awkward situation where you need to pass a dummy value to the 4th optional parameter so that you can a desired value to a 5th optional parameter)
(I guess these are the same reasons why it's useful on functions, really)
I notice that Mohsen Azimi (@mohsen1) asked about using it on constructors earlier in this thread and Daniel Rosenwasser (@DanielRosenwasser) kiboshed the idea unceremoniously (simply saying "It probably should not for now."). Maybe that's just because it was early on in the development of this refactoring and you all were trying to keep things as simple as possible? What extra complications do using this for constructors create that aren't present for functions?
Since this issue is old and closed should I open a new issue called "Make 'Convert parameters to destructured object' refactoring work for constructors"?
If anyone tried this and wonder why it doesn't work, it might be because you are selecting multiple lines.
Placing the cursor on just one parameter in a function and then opening the refactor menu should do it.
You seem to have been replying to my post, Axelhijacker76, but I'm not clear how your link was intended to help.
The answer in the link suggests using parameters in functions rather than writing a bunch of differently-named functions which could trivially be a single function with a parameter. Using parameters instead a having a bunch of similar copy-pasted slightly-different functions is fantastic advice of course, and is something that I do a lot of.
Adding more and more parameters, while better than writing many redundant functions, leads to the situation that I was described, that this refactoring is designed to help: a function (or constructor) which eventually gets too many parameters, not all of which are relevant in all situations, and they are annoying to have to pass all in the correct order, to plumb through multiple levels of the call stack, etc, and would be more simply expressed as a single configuration object which can be initialized to a decent set of defaults and overridden by setting the interesting members by name when needed.
Activity
DanielRosenwasser commentedon Apr 19, 2018
CC Axel Rauschmayer (@rauschma) Brenton Simpson (@appsforartists) in case you want to 👍
[-]Refactoring to convert to named parameters[/-][+]Refactoring to convert to "named parameters"[/+]rauschma commentedon Apr 20, 2018
Apologies for being ungrateful: this doesn’t reflect how I use this pattern – I don’t see the parameters as a whole, I see each parameter individually. As a work-around, I inline the type and don’t define it externally.
Let me try to convince you, one last time (then I’ll stop pestering you), of the usefulness of a better notation for destructuring (nested destructuring will profit, too!). The following is an extreme example, but there are many similar functions in my code (“Showoff” is the name of my slide framework):
With a better notation:
Alternatively:
Note how, in the last two examples, you actually see parameters with names (vs. a single type for all parameters). The first example looks messy, the last two examples don’t.
If you have ever used a programming language with named parameters and liked them there – isn’t this a compelling use case? Given the thumbs-up at a recent comment of mine, there are quite a few people who agree.
This is the only aspect of my plain JavaScript code that became less usable after I moved to TypeScript.
Kingwl commentedon Apr 20, 2018
a useful feature 👍
appsforartists commentedon Apr 20, 2018
I love that you're exploring this, but I agree with Axel Rauschmayer (@rauschma).
I think there's a bit of a cargo-cult bias in the TS community to always prefer interfaces (because they can be extended). Type literals seem like a more appropriate model for the kwargs pattern, because they allow users to treat each named argument individually.
If a third party made a "convert to named arguments" command, I would probably find it useful to be able to write function signatures normally and then convert them to an interface/type literal. However, I'd rather the language supported setting the type for a named arg adjacent to the declaration of its name and default value. It's both hard to read and cumbersome to maintain when the types are separated from the rest of the definition.
cancerberoSgx commentedon May 21, 2018
This refactor also must refactor all references to the method in the whole project right ? But a very helpful refactor , in my experience, happened many times when an API signature that needs to be backwards compatible, is defined with multiple params and then you keep adding parameters to implement new features or even change parameter type to OR, like existing: PreviousType|NewSemanticType....
Kingwl commentedon Jun 19, 2018
IMO, it should
cancerberoSgx commentedon Jun 19, 2018
So I was playing a lot lately with Language Service APIs and friends and I have several refactors more or less working fine. This is the one suggested here (I think) :
https://github.com/cancerberoSgx/typescript-plugins-of-mine/tree/master/typescript-plugin-proactive-code-fixes#transform-parameter-list-into-single-object-parameter
You can easily install them in vscode as an extension: https://marketplace.visualstudio.com/items?itemName=cancerberosgx.vscode-typescript-refactors
Probably TypeScript team will implement these more elegantly but in the meanwhile at least is fun. This is kind of a crazy tool that was really helpful to develop plugins quickly and learn the API: https://github.com/cancerberoSgx/typescript-plugins-of-mine/blob/master/typescript-plugin-ast-inspector/doc/evalCodeTutorial.md
And now I'm playing with some IPC communication between plugins in tsserver and host editor plugins /
extensions / packages that provide generic input-related operations - so I can inquire user for data in my refactors visually and keep being editor / IDE agnostic. For example, if I want to move a method to another class, or to another file I need to ask the user the destination and for that plugins can talk with these "Input Providers" generically. Since I'm manipulating the SourceFiles myself (not using FileEditRange ) I can do it synchronously. Really enjoying it will update you when I have something pretty to show.
mohsen1 commentedon Dec 5, 2018
This is great!
Have you thought about the default arguments with no types? Would you infer types there?
Also would it work in constructor function with
privateorpublickeyword? I think it won't, right?where would you generate a type name if function is anonymous?
AnyhowStep commentedon Dec 14, 2018
This would be super helpful to me, even if others would rather a new destructuring syntax for function parameters.
So, if the proposed refactoring somehow loses favour over new destructuring syntax, I would still like for this particular one to be implemented.
I personally use interfaces instead of inlining object parameter types, especially for large projects. And projects I write that get consumed by others.
I've had to wrap a function many times. And if the library doesn't have those function arguments as an interface, then I have to copy-paste their inlined code instead of just using an interface that should already be there.
People like to think of interfaces as being "code duplication". But in the big picture, not having those interfaces causes code duplication.
So, in my personal opinion, inlining = save time now, but cause code duplication in the future/for downstream users of your library. Interface = a little extra time now, but reduce code duplication and make it easier for downstream users to extend/reuse/wrap code.
DanielRosenwasser commentedon Dec 17, 2018
Mohsen Azimi (@mohsen1) you're now my favorite QA contact for refactorings 😉
Good test case! Should "fall out" from the naive implementation.
It probably should not for now.
As a first-pass, it's probably reasonable to say that this would only generate a named type for
We could always come back to this and add it for signatures like call type literals, construct type literals, call/construct signatures, and method signatures (i.e. the ambient stuff).
Kingwl commentedon Jan 10, 2019
ummmmmm, I'd like to work on it (If I didn't disrupt your release plan)
9 remaining items
RyanCavanaugh commentedon Mar 5, 2019
✨ 🚲 🏠 ✨
Naming poll! Vote:
OliverJAsh commentedon Mar 5, 2019
I call them named parameters, but just throwing another idea out there: "convert parameters to destructed object"
ShawnTalbert commentedon Mar 9, 2019
I wish we wouldn't encourage the pattern of making functions take a single object - it thwarts currying.
tjpalmer commentedon Mar 9, 2019
I know Axel Rauschmayer (@rauschma) said he'd stop and seems to have kept his word, but he's absolutely right that doesn't express what people want to express.
Have people discussed using
isyet? Example from the top using this syntax follows:And the TypeScript code base itself is chock full of long lists of parameters with optionals etc. Empirical evidence for where this could help.
brasten commentedon Sep 9, 2019
There were some discussions about
asin an earlier issue -- mostly involving concerns about introducing a new keyword in a very specific context. I suspectiswould have the same complaints.I haven't thought through every issue, but I'm wondering about using
:twice, where the renamed variable name can be optional. So instead of[object_key]: [variable_name] = [default_value]you end up with[object_key]: [variable_name]: [type] = [default_value]. This kind of makes sense since you're typing the variable name, rather than the object key, anyway.So the most verbose version of this would be:
... whereas the common case would be:
AnyhowStep commentedon Sep 9, 2019
There are cases where it is beneficial to take a single object with many parameters.
For example, that object can be an immutable data structure and the function is just performing some operation on it and returning a new instance of the data structure.
In some cases, those data structures can have dozens of properties... You're not really suggesting we curry in those cases as well, right?
jasonwilliams commentedon May 6, 2020
Daniel Rosenwasser (@DanielRosenwasser) what's the reason this was closed?
I was looking for this feature earlier and came across this issue.
RyanCavanaugh commentedon May 6, 2020
Jason Williams (@jasonwilliams) it was closed because it was implemented
uglycoyote commentedon Jan 5, 2022
I love this refactoring, but is there a a reason why it does not work on constructors?
I sometimes find myself creating a class where the number of constructor parameters grows out of control, with too many optional ones and I wished that I had instead made one "config" or "options" interface that can be passed in to the constructor. The advantages being:
(I guess these are the same reasons why it's useful on functions, really)
I notice that Mohsen Azimi (@mohsen1) asked about using it on constructors earlier in this thread and Daniel Rosenwasser (@DanielRosenwasser) kiboshed the idea unceremoniously (simply saying "It probably should not for now."). Maybe that's just because it was early on in the development of this refactoring and you all were trying to keep things as simple as possible? What extra complications do using this for constructors create that aren't present for functions?
Since this issue is old and closed should I open a new issue called "Make 'Convert parameters to destructured object' refactoring work for constructors"?
RyanCavanaugh commentedon Jan 5, 2022
Yes, please open a new issue. Thanks!
marco2216 commentedon Mar 29, 2023
If anyone tried this and wonder why it doesn't work, it might be because you are selecting multiple lines.
Placing the cursor on just one parameter in a function and then opening the refactor menu should do it.
uglycoyote commentedon Jan 31, 2025
You seem to have been replying to my post, Axelhijacker76, but I'm not clear how your link was intended to help.
The answer in the link suggests using parameters in functions rather than writing a bunch of differently-named functions which could trivially be a single function with a parameter. Using parameters instead a having a bunch of similar copy-pasted slightly-different functions is fantastic advice of course, and is something that I do a lot of.
Adding more and more parameters, while better than writing many redundant functions, leads to the situation that I was described, that this refactoring is designed to help: a function (or constructor) which eventually gets too many parameters, not all of which are relevant in all situations, and they are annoying to have to pass all in the correct order, to plumb through multiple levels of the call stack, etc, and would be more simply expressed as a single configuration object which can be initialized to a decent set of defaults and overridden by setting the interesting members by name when needed.