Utilizing TypeScript Generics for Creating Arrays of Objects with Inherited Type Definitions

I'm exploring the concept of type inheritance for an array of objects, where one object's value types should inherit from another. While I'm unsure if this is achievable, it's definitely worth a try. Currently, I believe my best approach would be to use an object instead of an array and work through it that way.

This example is based on a similar question asked by someone else.

In Example 1, towards the end, there's a discrepancy with the object key 'y'. It doesn't throw an error even though it's not part of the 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',
    },
  },
]);

Answer №1

It turns out that all you needed was to infer the array as a tuple:

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

And now it's working perfectly!

Check it out on the Playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide to setting up .env Variables on a DigitalOcean Ubuntu droplet

After deploying my Node.js app on a DigitalOcean Ubuntu droplet, I encountered the need for variables from my .env file. How can I go about creating these variables within the DigitalOcean droplet? ...

tsc will automatically incorporate additional files

I'm grappling with a frustrating issue related to tsc that's really getting to me. The problem involves having a file b.ts in the src folder, and another file a.ts in the project root folder. Here is an excerpt of my tsconfig file: { "comp ...

Tips for identifying the load window in Ionic 2 RC3 Angular 2

In the various services that I am using, I implement a method to load content. Each service has its own loadController to display a loading window. How can I detect if a loading window already exists in order to prevent showing a new one? *Please note: I ...

Encountering Typescript issues while trying to access @angular/core packages

Recently, I made an update to my Ionic app from Angular 7 to Angular 8, and an odd error popped up: The issue lies in the fact that I am unable to access any of the standard classes stored in the @angular/core module. This same problem is occurring with o ...

Developing a personalized validation function using Typescript for the expressValidator class - parameter is assumed to have a type of 'any'

I'm seeking to develop a unique validation function for express-validator in typescript by extending the 'body' object. After reviewing the helpful resource page, I came across this code snippet: import { ExpressValidator } from 'expre ...

Ways to verify the compatibility between TypeScript type definitions in @types and the corresponding package

After dabbling with typescript in my node.js projects for a while, I've come to realize that many npm packages have separate @types packages for typescript definitions. But here's the dilemma: how can one be certain that the @types package is syn ...

Which specific type should be utilized for the onchange event in checkboxes?

Which type should be used for checkbox event onchange when implementing pure javascript with typescript? const checkbox = document.querySelector("#myCheckbox") as HTMLInputElement; function handleCheckboxChange(event: ChangeEvent<HTMLInputEle ...

What is the best way to utilize yarn in order to install a GitHub package that utilizes TypeScript and has not yet been compiled?

After making modifications to an npm package and using yarn link <project name> locally, everything works perfectly. However, when pushing it to GitHub and trying to add it to the project with yarn add <repo url>#<branch> instead of yarn ...

What is the best way to effectively personalize my Bootstrap variables using SASS?

After creating a web page and adding Bootstrap styling, I attempted to customize the Bootstrap variables, but encountered an issue where it did not seem to work despite no errors being displayed. I followed a tutorial on YouTube meticulously, but to no av ...

What is the best way to modify the underline style of a tab in Material UI?

I'm trying to customize the underline of: https://i.stack.imgur.com/J2R1z.png Currently, I am using material ui version 4.12.3 The code snippet for generating my tabs is below: function renderTabs(): JSX.Element { return ( <Tabs className={cla ...

Angular2 encountering an unidentified Auth2 Object during logout process

Greetings, I am currently experiencing an issue with signing out of an auth2 client. Previously, this process functioned correctly until I upgraded my router to comply with new RC requirements. Now, it seems that the auth2 object is being cleared or lost ...

The error message "Property 'data1' is not a valid property on the object type {}"

const Page: NextPage = ({data1}:{data1:any}) => { const [open, setOpen] = React.useState(false); const [data, setData] = React.useState(data1); const handleAddClick = () => { setOpen(true); }; ..... } export async function getServerS ...

Display or conceal a button in Angular 6 based on certain conditions

In my current project, I am facing a challenge where I need to disable a button while a task is running and then activate it once the task is complete. Specifically, I want a syncing icon to spin when the task status is 'In_progress' and then hid ...

Can one extract a property from an object and assign it to a property on the constructor?

Currently working with TypeScript, I am looking to destructure properties from an object. The challenge lies in the fact that I need to assign it to a property on the constructor of the class: var someData = [{title: 'some title', desc: 'so ...

Tips for implementing Peer.js in Next.js using TypeScript?

Next.js has the ability to run on the server side, which can result in Peer.js throwing errors when used with Next.js. One user on Stack Overflow explains this issue: The error is likely due to peer js performing side effects during import. The suggest ...

XState: linking together multiple promises seamlessly without needing intermediate states

After reading the Invoking Multiple Services section, it seems that despite being able to invoke multiple promises, they appear to be triggered without waiting for the previous one to complete in my own tests. // ... invoke: [ { id: 'service1' ...

TypeScript in Angular causing lodash tree shaking failure

I am currently working on a large project that involves TypeScript. Various attempts have been made to optimize the use of lodash in my project. Before making any conclusions, I believe it is important to review the outcomes of my efforts. The command I ...

Comparison between typings and @types in the NPM scope

There are different approaches when it comes to handling TypeScript definitions. In some cases, the typings tool is used, as seen in projects like angular/angular2-seed. Alternatively, some projects use scoped NPM packages with the prefix @types, complete ...

Converting Javascript tools into Typescript

I'm currently in the process of migrating my Ionic1 project to Ionic2, and it's been quite an interesting journey so far! One challenge that I'm facing is how to transfer a lengthy list of utility functions written in JavaScript, like CmToFe ...

What is the proper way to access the global `angular` variable in Angular and TypeScript when using AngularJS?

I'm currently integrating an Angular 4 component into a large monolithic AngularJS application. The challenge I face lies in the restrictions of the AngularJS project's build system, which limits me to only modifying the project's index.html ...