1 Answers1

1

The trick is to convert over a generic type parameter not a specific type!

type Original = [number, string, Date]
type Converter<T> = T | undefined

type ConvertArray<T> = { [K in keyof T]: Converter<T[K]> }
type Final = ConvertArray<Original>


const fine: Final = [1, 'a', undefined]
const causesError: Final = ['a', 'a', 'a']
const alsoFine = fine.map(val => val)
const doubleLength = fine.length * 2
function fineFunc([num, str, date]: Final) {}
fineFunc(fine)
fineFunc(['a', 'a', 'a']) // error (correctly)

// If you don't use the generic intermediate step:
type WrongFinal = { [K in keyof Original]: Converter<Original[K]>}
const seemsOkRight: WrongFinal = [1, 'a', new Date()]
const actuallyWontWork = seemsOkRight.length * 2 // <- Error!!

Playground

Luke Miles
  • 941
  • 9
  • 19
  • But that's *not* a mapped tuple type; the resulting type isn't a tuple at all. You don't need to exclude non-index keys; see [this version of your code](https://tsplay.dev/wOP0lN). That's the whole point of [support for mapped tuple/array types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html#mapped-types-on-tuples-and-arrays). Can you [edit] or clarify? – jcalz May 31 '22 at 21:36
  • @jcalz I added playground link explaining why you want to exclude non-index keys and an example of how to add array methods back in – Luke Miles May 31 '22 at 22:46
  • If you have an explanation it should be in plain text in the answer itself in addition to any external link like a playground. Do you acknowledge that the output type you’re generating is not a tuple? I don’t see that explicitly mentioned anywhere here. – jcalz May 31 '22 at 23:08
  • @jcalz I removed the non-tuple answer and put the explanation directly in the text – Luke Miles May 31 '22 at 23:34
  • 1
    At the risk of repeating myself, the `Final` type isn't a real tuple type. An actual tuple type would be `[Converter, Converter, Converter]` like [this](https://tsplay.dev/w1ylkw). If you want to get a true tuple type out of a mapped type, you need to do it over a generic type parameter and not a specific type (at least until a fix for https://github.com/microsoft/TypeScript/issues/27995 ever shows up). I hope that you will at least acknowledge that a type like `{0: number} & number[]` is not a true tuple type like `[number]` is. – jcalz Jun 01 '22 at 01:19
  • 1
    Thank you so much @jca -- that is way better. I updated the answer. – Luke Miles Jun 02 '22 at 03:07