1

Given a function like this:

function makeObjects<T extends string[]>(...values: T);

Make the return value this:

T.map(v => ({ [v]: any }));

I'm using an array map to show what it looks like in my mind, yes I know that's not how TS works.

2
  • Is... this what you're looking for? If not, please consider clarifying your requirement, perhaps with some more words describing it, and some input-output examples. Right now you are using nonstandard/pseudo code and so I'm just guessing. Commented Apr 20, 2022 at 14:25
  • @jcalz this is exactly what I was looking for. Commented Apr 20, 2022 at 14:32

2 Answers 2

1

I'd give it the following call signature:

declare function makeObjects<T extends string[]>(...values: T):
  { [I in keyof T]: { [P in Extract<T[I], string>]: any } };

( Note that I have not implemented makeObjects(), and consider this to be outside the scope of the question as asked ).


The return type {[I in keyof T]: ...} is a mapped type over the keys of the generic input type parameter T. When T is an array or tuple type, the output type will also be an array or tuple type.

For each numeric-like index I from the input array type T, we want to use the element type T[I] as a key type. For this you need to use another mapped type, conceptually like {[P in T[I]]: any}, which means "an object type whose keys are T[I] and whose values are any". You could also express this as Record<T[I], any> using the Record<K, V> utility type.

Unfortunately while you only care about numeric-like indices, the compiler takes the view that I could be any key of T, including the array method names like "push" and "pop", and thus the property type T[I] could be all kinds of things you don't want to use as keys. (See ms/TS#27995 for a discussion of this issue).

The way to deal with that is to wrap T[I] in something the compiler will agree is definitely key-like. Since you only care about T[I] being a string (since T extends string[]), we can use the Extract<T, U> utility type to filter T[I] to just string-like things.

So that gives you { [I in keyof T]: { [P in Extract<T[I], string>]: any }}.


Let's test it out:

const foo = makeObjects("a", "b", "c");
// const foo: [{  a: any; }, { b: any; }, { c: any; }] 

Looks good; the output type is a tuple of objects whose keys come from the corresponding parameter to makeObjects().

Playground link to code

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

Comments

0

You can use the mapped type Record to create a type from the values in T:

function makeObject<T extends string[]>(...values: T): Record<T[number], any>{
    return null!
}

let x = makeObject("a", "b", "c")

x.a
x.d // err

Playground Link

2 Comments

Yeah this could be what OP wants (especially given the name makeObject), but "tuple of objects" in the title makes me think it might be what I put in my comment. Not sure, though.
I made a mistake when writing the code example, It should say "makeObjects" the thing I want is a tuple of objects mapped by the array.

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.