typescripttypescript-genericsnested-generics

How can I obtain the correct types for an array or object based on an array of object nested keys with generics in typescript?


I first generate suggestion keys with object keys that can be used like this

const data = {
 content: {
  value: "string", 
 },
 anotherContent: {
  anotherValue: 20
 }
}.

const result = function(["content.value","anotherContent.anotherValue"])

First, I wanted the result type to be {value: number, otherValue: number}.

So, I type it like this.

<Target, Suggestion extends DotedKeysType<S>[]>(
    target?: Target extends Suggestion ? Target : Suggestion 
  ): {
    [key in Target extends Suggestion ? Target[number] : string]: Target extends Suggestion 
      ? OutputByKeys<S, key>
      : never;
  };

Everything works fine, except for the following.

<View>{result.content}</View>
<View>{result["anotherContent.anotherValue"]}</View>

I also realize that even if we made it, we will have a key problem

// for a data like this. Both last keys are named 'value'
const data = {content: {value: 10},anotherContent: {value: 20}}

// Keeping the result as an object override the value key in the object.
// Since objects can not have same key as property
const result = function(["content.value","anotherContent.value"]);
result = {value : 20 // `Override value of content with anotherContent`}

But with array as an result, Each item in the array can have different name. For example

result = [myFirstValue, mySecondValue] = function(["content.value","anotherContent.anotherValue"])

With that the type of myFirstValue must be the type of content.value and the type mySecondValue must be the type of anotherContent.anotherValue.

So far, that's how close I've come.

 <Target, Suggestion extends DotedKeysType<S>[]>(
    target?: Target extends Suggestion ? Target : Suggestion 
  ): OutputByKeys<S, Target extends Sug ? Target[number] : keyof S>[];

This gives me a union type of all the keys in the target array.

result = [value1: string | number, value2: string | number]

Is there a way to get a type like this in case of array?

// type of `content.value` is `string`
// type of `notherContent.anotherValue` is `number`

const result = function(["content.value","anotherContent.anotherValue"]);

result =>  [myFirstValue, mySecondValue]

result type will be [value1: string, value2: number]

And in case of object where last key are different

// type of `content.value` is `string`
// type of `anotherContent.anotherValue` is `number`
const result = function(["content.value","anotherContent.anotherValue"])

result => {value: "string", anotherValue: 20}

result type => {value: string, anotherValue: number}

I do not want an intersection if possible

// i don't want that if possible
result type => [string & number, string & number]

But i want this

result type => [string, number]

I can't predict the user's choice, otherwise I'd just set the array type => [string, number] and my problem would be solved. I've read a lot of answers but I still haven't found the one I need.

I ask for the same thing in different ways. For an object because it might be useful in another project and for array because it's my current need.


Solution

  • Hope this can help you. result here

    type Mutable<T> = {
      -readonly [P in keyof T]: T[P];
    };
    
    type GetProperty<T, S> = S extends `${infer K}.${infer R}`
      ? K extends keyof T
        ? GetProperty<T[K], R>
        : any
      : S extends keyof T
      ? T[S]
      : any;
    
    type GetPropertyFromArray<T, A> = Mutable<A> extends [infer K, ...infer R] ? [GetProperty<T, K>, ...GetPropertyFromArray<T, R>] : A;
    
    function fn<T extends {}, A extends {}>(keys: A): GetPropertyFromArray<T, A> {
      return 0 as any;
    }
    
    const data = {
      content: {
        value: 'string',
      },
      anotherContent: {
        anotherValue: 20,
      },
    };
    const keys = ['content.value', 'anotherContent.anotherValue'] as const;
    const res = fn<typeof data, typeof keys>(keys);