0

I am trying to create a helper function that simplifies creating lookups.

So given this:

const obj = produceLiteral([`foo`, `bar`, `foo`, `moo`]);

obj should be typed as:

interface Obj {
  foo: `foo`;
  bar: `bar`;
  moo: `moo`;
}

Not sure how.

5
  • How can that simplify lookups ? Commented Jul 15, 2021 at 3:35
  • I like to create ids such that I can do myIds.foo this will return foo. This is typesafe and non-magic thus can be shared around the codebase Commented Jul 15, 2021 at 3:43
  • Will the values in the array be known at compile time ? or it's only known at runtime ? Commented Jul 15, 2021 at 3:44
  • Known at compile time (tuple) Commented Jul 15, 2021 at 3:53
  • There's nothing more type safe or less magic about this than just using the string literals directly. In fact, using them directly is even less to type. Type safety comes from whatever is accepting these values to be typed correctly, so having a union type of the possible literal values should suffice. Commented Jul 15, 2021 at 4:10

3 Answers 3

1

Assuming you have a tuple ['foo', 'bar', 'foo', 'moo'], here is one way to derive the needed interface:

const tuple = [`foo`, `bar`, `foo`, `moo`] as const

type Obj = { [K in typeof tuple[number]]: K }

playground

To create the actual object:

const tuple = [`foo`, `bar`, `foo`, `moo`] as const

type Obj = { [K in typeof tuple[number]]: K }

const obj = tuple.reduce((acc, val) => ({ ...acc, [val]: val }), {}) as Obj

playground

Sign up to request clarification or add additional context in comments.

Comments

1

Thanks to "lukejohn" on the Fenders Slack for this answer:

function produceLiteral<Key extends string>(keys: Key[]): {[key in Key]: key} {
    const literal: Partial<{[key in Key]: key}> = {}

    for (const key of keys) {
        literal[key] = key
    }

    return literal as {[key in Key]: key}
}

const obj = produceLiteral([`foo`, `bar`, `foo`, `moo`]);

Playground link: https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABABwE5wCYggUwDIxQ6oCGANgDwDSOAnojgB5FgYDOibUqMYA5gD4AFAGs6bAFyIatANoBdAJRSA3rLH1e0uvKkaAvohUAoRGcQQEXRGULFyUgAolUschTUbEWmbsQGBRABeI31jU3NgOFREIUsway84YH9xRSMI83NbIlIydR1g1NpMxDDS1BwoEFQkHPsyRBIOTzpvJF89OjDy+Os4ACMAKyK0TGx8OzyhWQADKLhZgBpEWYGXZdWFzdmAWzhFpQBuYyA

Comments

0

Not completely sure, but I think what you are looking for is:

const produceLiteral = (input: string[]): Obj => input.reduce((acc, curr) => ({...acc, [curr]: curr} as Obj), {})

But maybe typing will become a problem if the input array changes, I am not completly sure but maybe a more generic interface like

interface Obj {
   [key: string]: string
}

1 Comment

No I want TS to infer input and generate Obj type for me

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.