#Mapped type works fine on type level, but not when thrown into code

9 messages · Page 1 of 1 (latest)

dusty pilot
#

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?

pulsar tinsel
#

!pg

foggy tinselBOT
pulsar tinsel
#

there is no type-safe way to express "this is the key corresponding to T"

#

especially if key isn't known at typecheck time

#

and especially especially if TArr isn't known (since it's a generic parameter)

foggy tinselBOT
pulsar tinsel
#

!ts