I have challenged myself with a mapped type, and while the type seems to be ok, the code that uses it - not so much. And I have run out of ideas, seems like I'm missing some useful tool in my toolbelt.
The purpose is to convert an array of strings into an object whose keys are derived from splitting each string by dot and taking the last member:
some.very.long.path becomes path
type RightmostDot<T extends string> = T extends `${infer _}.${infer U}`
? RightmostDot<U>
: T;
type KeyedObject<T extends string[]> = {
[K in T[number] as RightmostDot<K>]: string;
}
// TYPE LEVEL - TEST
type KeysArray = [
'some.very.long.path',
'mypath',
'another.path.with.a.long.name',
'yet.another.path' // will override the former 'path', but I am ok with that
]
type test = KeyedObject<KeysArray>;
The test type looks beautifully, just as intended.
Now when I move over to code, TS is no longer happy with this reduce and indexing acc
function getKeyedObjectFrom<TArr extends string[]>(arr: TArr): KeyedObject<TArr> {
return arr.reduce((acc, curr) => {
const key = curr.split('.').pop()!; // I hate this ! but had to use it
acc[key] = ''; // <- expression of type 'string' can't be used to index type 'KeyedObject<T>'
return acc;
}, {} as KeyedObject<TArr>);
}
const tx = getKeyedObjectFrom([
'some.very.long.path',
'mypath',
'another.path.with.a.long.name',
'yet.another.path',
])
Been trying throwing & string in different parts, as well as coercing as ${RightmostDot<K>} but nothing seems to make TS happy. Any ideas on how to solve it?