0

I'd like to have an object where the string literal keys of that object must be found in the value object of that object. It’s easier to show the desired output:

const parts: PartMap = {
  foo: { value: 'foo' }  // this would be valid,
  bar: { value: 'cats' } // this would be invalid
}

Is this possible without passing a full generic of the same object PartMap? Using information from this SO answer I've come up with something "close", but it doesn't work properly because it appears the PartMap type is not communicating the string literal to the Part type:

type StringLiteral<T> = T extends string ? string extends T ? never : T : never;

interface Part<T extends string> {
  value: StringLiteral<T>
}

interface PartMap {
  [index: string]: Part<typeof index>
}

const x: PartMap = {
  // ⚠️ Type 'string' is not assignable to type 'never'.
  foo: { value: 'foo' }
}

TS playground link

jpschroeder
  • 6,636
  • 2
  • 34
  • 34

1 Answers1

1

no, you can't do this without generic because typeof index is defined and fixed as string before you use it.

try to create a noop function to let the compiler infer object type like this:

type PartMap<T extends string> = {
  [K in T]: Part<K>;
};

const x = assertLiteralPartMap({
  foo: { value: 'foo' }
})

function assertLiteralPartMap<T extends string>(x: PartMap<T>) {
  return x
}
Jerryh001
  • 766
  • 2
  • 11