If in TypeScript your function takes a dictionary of parameters, I often end up writing code like this:
public doStuff({ arg1, arg2, arg3, arg4 }: { arg1: string, arg2: number, arg3: number, arg4?: boolean })
which is not particularly DRY.
My suggestion would be to write the above code like this:
public doStuff({ arg1: string, arg2: number, arg3: number, arg4?: boolean })
I think this would make a lot of code easier to read. Thoughts?
@johnfn unfortunately this is impossible because the the syntax you propose is already in use. It destructures the incoming object, declaring locals having the names to the right of the :s aliasing the properties to their left.
I have no idea why this syntax was chosen by TC39 given that the as keyword is used for destructuring imports into local aliases, which is conceptually analogous.
I wonder if anyone can shed some light on why this syntax was chosen.
I have no idea why this syntax was chosen by TC39
No idea here either :confused:
Coincidentally, @bterlson and I had this conversation earlier today. Still no answer. :smile:
that the as keyword is used for destructuring imports into local aliases
Indeed it is for imports. However for general destructuring it is consistent with structuring and uses : (and parameters follow the same pattern) (wrote it up once).
// structure
const obj = {"some property": "some value"};
// destructure
const {"some property": someProperty} = obj;
Everyone's dislike for the es6 import syntax has been discussed before ¯\_(ツ)_/¯ :rose:
Alright - my poor attempts at defining a new syntax aside, I think we're getting away from the main point, which is the violation of DRY by the duplication of argument names. I'm pretty sure that faux-keyword-arguments is a fairly common pattern that leads to code that could look nicer. I also figure that if we had a discussion, we could work out a non-clashing syntax.
Furthermore, since this is a problem specific to TypeScript - more precisely, the typings that TypeScript gives to the arguments, it seems like it's fairly well within scope. (Unless I misunderstood something?)
Thoughts?
@johnfn I agree the syntax could use improvement, it's pretty bad as it is, I just have no idea how to improve it. I almost never write functions that take advantage of this language feature for all the reasons you mention. I do use it for callbacks, when the type can be inferred, but nowhere else.
@basarat I agree that there is a desirable symmetry but I don't think that outweighs the fact that the syntax is a bit odd in that it reverses the LTR order of assignment. Imagine if JavaScript had used =s instead of :s for object literals (lots of precedents) when it was first designed... I actually like the as keyword because one its literal meanings in English is alias.
public doStuff({ arg1 implements string, arg2 implements number, arg3 implements number, arg4? implements boolean })
Just syntax-bikeshedding.
@DanielRosenwasser I don't recall that conversation :-P But it's true I don't entirely grasp the rationale. I am guessing that it was important that a destructuring pattern match the grammar of object literals. It definitely seems nicer than as with nested destructuring, ie let {x: {y: z}} = {x: {y: 1}} over let {x as {y as z}} = {x: {y: 1}}. You could argue that binding to a nested pattern uses : but renaming uses as, though this doesn't resolve the ambiguity for TS and introduces an inconsistency for arguable gain. Additionally, because you can make use of the { x } shorthand, it is useful to know this is always a shorthand for { x: x } even in destructuring patterns.
Since we're already talking about it, maybe a syntax like this would be acceptable?
public doStuff({arg1: string, arg2: number, arg3: number, arg4: boolean } as params)
The as would give a strong hint as to what's going on.
@johnfn but you still have the problem that you can't express an inline rename and the type at the same time.
Also that syntax could break compatibility with a future version of ECMAScript which, for example, could introduce a feature that allows simultaneously destructing an object to access specific fields but allowing the entire structure to be named.
@bterlson when nested destructuring patterns come into play, the symmetry of the current syntax is really quite elegant. It wins me over. Actually, I'm curious why the : syntax is not leveraged, by analogy, for aliasing module exports. Why was as introduced? Does it stem from a preference for Python?
@aluanhaddad
If you want the inline rename you can just type out the original syntax. It seems like an acceptable compromise to me.
Also, ALL of typescript's syntax could conflict with a new version of ECMAScript. I'm not sure how this syntax is a special case.
Most helpful comment
@DanielRosenwasser I don't recall that conversation :-P But it's true I don't entirely grasp the rationale. I am guessing that it was important that a destructuring pattern match the grammar of object literals. It definitely seems nicer than
aswith nested destructuring, ielet {x: {y: z}} = {x: {y: 1}}overlet {x as {y as z}} = {x: {y: 1}}. You could argue that binding to a nested pattern uses:but renaming usesas, though this doesn't resolve the ambiguity for TS and introduces an inconsistency for arguable gain. Additionally, because you can make use of the{ x }shorthand, it is useful to know this is always a shorthand for{ x: x }even in destructuring patterns.