In this code example, the expandedMappedTuple
does the same operation as the looseMappedTuple
.
However, the looseMappedTuple
has lost all information regarding the length and tuple sequence.
Is there a MappedTupleType
which can fix this which could be added to the draft mapping code which follows, and cast the return value of .map
with extra information from the original readonly tuple?
Alternatively is there another way to ensure strictMappedTuple
has the same type as expandedMappedTuple
?
Unlike with https://stackoverflow.com/a/66091730/2257198 the mapping is defined in MappedType
and can be baked into the mapTuple
function, (no higher-order types needed) and I am happy to use type assertions when the facts are so certain - I don't expect it to be inferred.
However, I've tried a lot of ways to define MappedTupleType and so far failed.
This is a deliberately toy example representing a real world problem from a list of constants I want to transform, preserving type and length.
type MappedType<T extends string> = `£${T}`;
function prefixCurrencyString<T extends string>(value: T): MappedType<T> {
return `£${value}`;
}
const letters = ["a", "b", "c"] as const;
// Type of this const is correctly
// readonly ["£a", "£b", "£c"]
// with length and ordering
const expandedMappedTuple = [
prefixCurrencyString(letters[0]),
prefixCurrencyString(letters[1]),
prefixCurrencyString(letters[2]),
] as const;
// Type of this const is sadly
// ("£a" | "£b" | "£c")[]
// without length or ordering
const looseMappedTuple = letters.map(prefixCurrencyString);
/** TRY AND FIX IT */
// The aim of this type would be to 'cast' the array returned from .map
// as used in mapTuple() below
type MappedTupleType<Tup extends [...string[]]> = {
[Index in keyof Tup]: MappedType<Tup[Index]>;
} & {length: Tup['length']}
function mapTuple<
Tup extends Readonly<[...string[]]>,
Fn extends <T extends string>(t: T) => MappedType<T>
>(list: Tup, fn: Fn) {
const mappedList = list.map(fn);
type Arr = typeof mappedList;
return mappedList as MappedTupleType<Tup>;
}
// Type of this const should be
// readonly ["£a", "£b", "£c"]
// just like expandedMappedTuple
const strictMappedTuple = mapTuple(letters, prefixCurrencyString);