0

I am trying to get type inheritance for an array of objects where one of the object value types should be inheriting types from another object value. I have my doubts if this is possible but it's worth a shot. Right now I think my best bet is to use an object instead of an array an solve it like that.

The example is based on the answer of someone asking something similar.

All the way at the bottom of the example 1, it does not give an error for the object key 'y'. This should give an error as it's not in initialValue.inherit object.

// Example 1

type FinalValues<T extends Array<{ initialValue: { inherit: any } }>> = {
    [P in keyof T]: T[P] extends { initialValue: infer I extends { inherit: any } } ? { initialValue: I, finalValue: I['inherit'] }: never 
}

function myFunc<T extends [{ initialValue: { inherit: any } }] | Array<{ initialValue: { inherit: any } }>>(v: T & FinalValues<T>) {

}

myFunc([
  {
    initialValue: { inherit: { x: 6 } }, // number
    finalValue: { x: 6 }, // number
  },
  {
    initialValue: { inherit: { y: "hello" } }, // string
    finalValue: { y: "bye" }, // string
  },
]);

myFunc([
  {
    initialValue: { inherit: { x: "hello" , y: 1} }, // string/number
    finalValue: { x: 6, y: 1 }, // err (x should be a string)
  },
  {
    initialValue: { inherit: { a: 'hello' } }, // string
    finalValue: { a: 6,  }, // err (a should be a string)
  },
  {
    initialValue: { inherit: { z: 'hello' } }, // string
    finalValue: { y: 1, z: 'hello' }, // this doesnt error but it should (y is not in initialValue.inherit) 
  },
]);

// Example 2

interface TypeOne {
  options: { someBool?: boolean; someString: string };
}
interface TypeTwo {
  options: { otherKeyBool: boolean };
}

const exampleOne: TypeOne = {
  options: { someBool: true, someString: 'hello' },
};
const exampleTwo: TypeTwo = { options: { otherKeyBool: true } };

interface PassedOptionsType {
  options: Record<string, number | boolean | string>;
}

type ConsumerArrayType<T extends PassedOptionsType[]> = {
  [K in keyof T]: {
    passedOptions: T[K];
    typedBasedOn: T[K]["options"];
  };
};

const consumerFn = <T extends PassedOptionsType[]>(arr: ConsumerArrayType<T>) => null;

consumerFn([
  {
    passedOptions: exampleOne,
    typedBasedOn: {
      // is valid:
      someString: 'valid string',
      // errors correctly:
      unknownKey: 'bla', // invalid key
    },
  },
  {
    passedOptions: exampleTwo,
    typedBasedOn: {
      // is valid:
      otherKeyBool: true,
      
      // is NOT working as expected as its an object key
      // of exampleOne.options and not of exampleTwo.options
      // this should give an type error
      someString: 'invalid type',
    },
  },
]);
2
  • 2
    I don't understand what and what shouldn't error here... "inherits types from all passed objects" is pretty vague as well; could you clarify the question and expectations? Commented Oct 20, 2022 at 14:57
  • Perhaps example 1 is more clear (updated to be identical to the answer I found on someone else his question) Commented Oct 20, 2022 at 15:30

1 Answer 1

1

Turns out you just needed the array to be inferred as a tuple:

const consumerFn = <T extends PassedOptionsType[]>(arr: [...ConsumerArrayType<T>]) => null;

Now it works correctly.

Playground

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.