question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

suggestion: explicit "tuple" syntax

See original GitHub issue

Problem

I’m writing this after encountering (what I think is) #3369. I’m a noob to TS, so I apologize for any misunderstandings on my part. The behavior the lack of type inference here:

interface Foo {
  bar: [number, number];
}

interface McBean {
  baz: Foo;
}

// error: McMonkey does not implement McBean
class McMonkey implements McBean {
  baz = {
    bar: [0, 1]
  };
}

// vs

// no error
class McMonkey implements McBean {
  baz: Foo = {
    bar: [0, 1]
  };
}

Because array literals are (correctly) inferred to be arrays, TS is limited in its ability to infer “tuple”. This means there’s an added overhead to working with tuples, and discourages use.

As I see it, the problem is that a tuple is defined using array literal notation.

A Conservative Solution

Adding a type query (?) such as tuple (.e.g tuple) would be better than nothing:

class McMonkey implements McBean {
  baz = {
    bar: <tuple [number,number]> [0, 1]
  };
}

…but this is still clunky, because you’d have to use it just as much as you’d have to explicitly declare the type.

A Radical Solution

There’s a precedent (Python) for a tuple syntax of (x, y). Use it:

interface Foo {
  bar: (number, number);
}

interface McBean {
  baz: Foo;
}

class McMonkey implements McBean {
  baz = {
    bar: (0, 1)
  };
}

Obvious Problem

The comma operator is a thing, so const oops = 0, 1 is valid JavaScript. 0 is just a noop, and the value of oops will be 1. Python does not have a comma operator (which is meaningful in itself).

I’ve occasionally used the comma operator in a for loop, similar to the MDN article. Declaring var’s at the top of a function is/was common:

function () {
  var a, b, c;
}

Parens are of course used in the syntax of loops and conditionals, as well as a means of grouping expressions.

A nuance of Python’s tuples are that they must contain at one or more commas, which could help:

foo = (0) # syntax error or just `0`; can't recall
foo = (0,) # valid tuple

…or it could actually make matters worse, because (0, ) is an unterminated statement in JavaScript.

That all being said, using the suggested Python-like syntax, it seems difficult but possible for the compiler to understand when the code means “tuple” vs. when it doesn’t.


I’d be behind any other idea that’d achieve the same end. I don’t know how far TS is willing to diverge from JS, and I imagine significant diversions such as the above are not taken lightly.

(I apologize if something like this has been proposed before; I searched suggestions but didn’t find what I was looking for)

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:9
  • Comments:29 (17 by maintainers)

github_iconTop GitHub Comments

7reactions
rauschmacommented, Dec 28, 2019

@RyanCavanaugh as const worked well in my case (where I needed to pass on a complicated mapped type).

Possibly: Allow as tuple as an alternative syntax when the operand is an Array. Rationale: Expresses the intent better.

4reactions
crdrostcommented, Jun 8, 2018

The proposed “radical solution” would mean that in certain circumstances TypeScript is no longer a superset of JavaScript, so it is probably a no-go.

However we could maybe steal F#'s array syntax.

Note that there is a very common circumstance where I’d like to use this, namely map initializers. Right now I have to write in several places,

// in a context where myObjs :: IObject[] and IObject extends {name: string}
const myDict = new Map<string, { seen: boolean, obj: IObject }>();
for (const obj of myObjs) {
  myDict.set(obj.name, { seen: false, obj });
}
// do something that might "see" an obj in myDict
// update the ones that were "seen"
// then remove ones that were not "seen"

Note that I am not using the array initializer because it makes the code much less readable and possibly might hide type errors with the coercive weight of as:

const myDict = new Map(
  myObjs.map(obj => [ obj.name, { seen: false, obj } ] as [ string, { seen: boolean, obj: IObject } ])
);

note that there’s two sources of noise here, the intrinsic noise of the map and the unnecessary noise of telling TypeScript that I meant to write a [string, object] tuple not a (string | object)[] array, and either one of them is readable on its own (the type declaration is no more complex than the type above) but it’s that they have to be joined together which makes this look jarring. So the above keeps the type declaration in exchange for a loop.

If we allow [| ... |] for a tuple constructor, we could do the reverse:

const myDict = new Map(
  myObjs.map(obj => [| obj.name, { seen: false, obj } |])
)

Note that | being a binary operator cannot legally appear directly before ] or after [ so it should preserve the superset-ed-ness?

Read more comments on GitHub >

github_iconTop Results From Across the Web

typing — Support for type hints — Python 3.11.1 documentation
Introducing syntax for annotating variables outside of function ... str] Address = tuple[str, int] Server = tuple[Address, ConnectionOptions] def ...
Read more >
Essential .NET - C# 7.0: Tuples Explained | Microsoft Learn
Suggestions include using parameter-naming conventions when the tuple behaves like a parameter —such as when returning multiple values that before tuple syntax ...
Read more >
Code Syntax Style: Implicit/Explicit Typing ('var' Keyword)
Starting from C# 7.0, you can declare local variables when deconstructing tuples. If you prefer var in such declarations, ...
Read more >
Documentation - TypeScript 4.0
The first change is that spreads in tuple type syntax can now be generic. ... an explicit type annotation along with a definite...
Read more >
Write Pythonic and Clean Code With namedtuple - Real Python
You can create named tuples that contain mutable objects. You can modify the mutable objects in the underlying tuple. However, this doesn't mean...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found