-1

Let's say I want my function to a simple Typescript 2-tuple:

type SuperTuple = [number, string]
function torgle(value: string): SuperTuple { 
  return [value.length, value]
}

This allows me to conveniently destructure the return value of torgle:

const [index, label] = torgle('pizza')
>> [5, 'pizza']

But I also want to define a special sort of map function that operates on the first value (number) only, and passes through the second one:

const hmmm = torgle('pizza').mapIndex(i => i * i)
>> [25, 'pizza']

In Javascript, I can add the mapIndex function directly, so I can almost make it work like this:

function supTup(num: number, str: string): SuperTuple {
    const tup: [number, string] = [num, str]
    return Object.assign(tup,
        {mapIndex: (f: (x: number) => number) => supTup(f(num), str)} 
    )
}

function torgle(value: string): SuperTuple {
    return supTup(value.length, value)
}

But Typescript is displeased. It might conceivably let me use a generator or class extending Iterator, but I'm not quite sure what that would look like in this case.

How can I type SuperTuple and/or torgle so I can both destructure SuperTuple as an array and call my custom method on it?

const [string1, label1]: SuperTuple = torgle('pizza')
const [string2, label2]: SuperTuple = torgle('pasta').mapIndex(i => i * 2)
Sasgorilla
  • 2,403
  • 2
  • 29
  • 56
  • 1
    Could you pass this example code through an IDE to make sure it's a [mre]? I think `string` is not the name of your parameter but you're using it as such... and `(f: number => number)` is also not valid TS. AND `reverse()` is not a method of `string`. AAAAAND `tuple.mapIndex` doesn't return a `SuperTuple` but you're treating it as such. Could you... like, fix these? – jcalz Nov 15 '22 at 01:04
  • 1
    [This code](https://tsplay.dev/WPpXzW) is how I might implement what you seem to be asking for, but I had to fix so many things about the code that it's almost not even the same example anymore. If you decide to [edit] your code to something like [this](https://tsplay.dev/w8xk4w) which has typing problems but no other issues, and want me to answer accordingly, please let me know by mentioning @jcalz in a reply. – jcalz Nov 15 '22 at 01:15
  • @jcalz You are right, I got a little sloppy here. Edited to remove (most?) syntax errors / typos. But at any rate you figured it out what I was trying to say -- [your example](https://tsplay.dev/WPpXzW) is what I was looking for! – Sasgorilla Nov 15 '22 at 13:28
  • In JS, you could make an iterable object, but I would recommend not to do that with different element types - and I don't think this would work in TypeScript. – Bergi Nov 15 '22 at 13:35
  • You can define `type SuperTuple = [number, string] & {mapIndex: (this: SuperTuple, f: (i: number) => number) => SuperTuple}` – Bergi Nov 15 '22 at 13:36
  • 1
    In your updated example, you seem to want that `torgle('xxx').mapIndex(cb)` will also return a `SuperTuple`, but your implementation absolutely does not do that, since the returned value has no `mapIndex()` method itself. That's why I gave you [this code example](https://tsplay.dev/w8xk4w) which has no implementation issues. If you can't use that code directly, could you tell me why not? I really doubt you're looking for an answer that explains how to do the JavaScript part, since you are saying you already know how to do that. Please get back to me (via @jcalz) to let me know what to do. – jcalz Nov 15 '22 at 16:09
  • @jcalz Ah yes, you are right. I updated the answer to better explain what's wrong with my version. I'm happy to use your (correct) code, but at that point I'm pretty much just replacing my question with your answer. I thought you might prefer posting your code as an answer, instead -- either way you're more than welcome to edit my post as you see fit. – Sasgorilla Nov 15 '22 at 19:55
  • 1
    @jcalz on second thought, sure, I'll just replace that part with your implementation. Same still goes on the editing -- feel free to make any additional edits to my post you think are necessary. – Sasgorilla Nov 15 '22 at 20:03

1 Answers1

2

You can define SuperTuple as the intersection of a plain [number, string] tuple with an object type containing the mapIndex() method:

type SuperTuple = [number, string] & 
  { mapIndex(f: (x: number) => number): SuperTuple };

In English I would read that as: "a SuperTuple is both a [number, string] and something with a mapIndex() method that takes a number-to-number callback and returns another SuperTuple.

And now the compiler will stop complaining:

const hmmm = torgle('pizza').mapIndex(i => i * i); // okay
//const hmmm: SuperTuple

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360