Determining the parent type in Typescript by inferring it from a nested member

Typescript has the ability to infer the type of a value based on queries made within if statements. For instance, the type of one member of an object can be deduced based on another:

type ChildType = 'a' | 'b';

type Child<T extends ChildType> = T extends 'a' ? { type: 'a', color: 'blue' } : T extends 'b' ? { type: 'b', color: 'red' } : never;

interface Parent<T extends Child<ChildType>> {
    child: T extends Child<infer R> ? Child<R> : never;
}


function test<T extends Parent<Child<any>>>(parent: T) {
    if (parent.child.type === 'a') {
        parent.child.color === 'red'; // raises error as it should be blue
    }
}

However, when trying to achieve the same functionality by checking nested members of a type, the behavior seems different.

type ChildType = 'a' | 'b';

interface BaseParent<T extends Child<ChildType>> {
    child: T;
}

interface Child<T extends ChildType> {
    type: T;
}

type Parent<T extends ChildType>
    = T extends 'a' ? { color: 'blue' } & BaseParent<Child<'a'>>
    : T extends 'b' ? { color: 'red', opacity: 2 } & BaseParent<Child<'b'>>
    : never;

function test<T extends Parent<ChildType>>(parent: T) {
    if (parent.child.type === 'a') {
        parent.color === 'red'; // no error thrown even though there should be
    }
}

This issue can be resolved using type guards, however I am exploring alternative methods that do not require them.

Answer №1

Initially, it's important to note that having parent data dependency on its child may not align with proper architecture principles. Generally, dependencies should flow downwards rather than upwards. It is crucial to consider this aspect from the beginning.

However, if you still wish to pursue this approach, one way of implementing it could be illustrated through the following example involving animals:


type AcceptedAnimal = 'dogs' | 'cats';

interface ParentBase {
  accepts: AcceptedAnimal
}

interface Cat {
  meows: boolean;
}

interface Dog {
  race: string;
}

type Parent = DogOwner | CatOwner;

interface DogOwner extends ParentBase {
  animal: Dog
}

interface CatOwner extends ParentBase {
  animal: Cat;
}

function isDogOwner(parent: ParentBase): parent is DogOwner {
  return parent.accepts === 'dogs';
} 

function test(parent: Parent) {
  if (isDogOwner(parent)) {
    parent.animal.race; // This is okay for a dog owner
    return;
  }

  parent.animal.meows; // This is acceptable for a cat owner
}

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

Data object constructor is not triggered during JSON parsing

Currently, I am retrieving data from a server and then parsing it into TypeScript classes. To incorporate inheritance in my classes, each class must be capable of reporting its type. Let me explain the process: Starting with the base class import { PageE ...

Visual Studio Code is unable to identify the TypeScript module located within the `node_modules` directory

After cloning the tslint repository, running npm install and grunt as instructed in the README, I opened the folder in Visual Studio Code (0.9.1). Upon inspecting a .ts file such as src/rules/typedefRule.ts, TypeScript errors appeared regarding the require ...

Could someone teach me how to implement icon rotation in Vue.js using Vuetify?

I am currently working on adding a feature where the icon rotates when the button is clicked, moving from down to up and vice versa in a spinning motion. Here is the code I have so far: <template> <v-btn v-on:click="isShow = !isShow" ...

Tips for implementing a decorator in a TypeScript-dependent Node module with Create-React-App

I am working on a project using TypeScript and React, which has a dependency on another local TypeScript based project. Here are the configurations: tsconfig.json of the React project: "compilerOptions": { "target": "esnext& ...

What is the correct way to import scss files in a Next.js project?

Currently, I am working on a NextJS project that uses Sass with TypeScript. Everything is running smoothly in the development environment, but as soon as I attempt to create a build version of the project, I encounter this error. https://i.stack.imgur.com ...

How should I properly initialize my numeric variable in Vue.js 3?

Encountering an issue with Vue 3 where the error message reads: Type 'null' is not assignable to type 'number'. The problematic code snippet looks like this: interface ComponentState { heroSelected: number; } export default define ...

Converting a unix timestamp to a Date in TypeScript - a comprehensive guide

To retrieve the unix timestamp of a Date in plain JavaScript and TypeScript, we can use this code snippet: let currentDate = new Date(); const unixTime = currentDate.valueOf(); Converting the unix timestamp back to a Date object in JavaScript is straight ...

Switching the keyboard language on the client side of programming languages

I'm interested in altering the keyboard language when an input element changes. Is it possible to modify the keyboard language using client-side programming languages? And specifically, can JavaScript be used to change the keyboard language? ...

What is the process for transferring a Pulumi Output<T> to the container definition of a task in ECS?

When creating a generic ECS service that deals with dynamic data, it is important to note that the containerDefinition within a Task Definition must be provided as a single valid JSON document. The code snippet for this setup looks like: genericClientServi ...

The MUI datagrid fails to display any rows even though there are clearly rows present

Today, I encountered an issue with the datagrid in Material UI. Despite having rows of data, they are not displaying properly on the screen. This problem is completely new to me as everything was working perfectly just yesterday. The only thing I did betwe ...

Utilizing ngFor to iterate over items within an Observable array serving as unique identifiers

Just starting out with Angular and I'm really impressed with its power so far. I'm using the angularfire2 library to fetch two separate lists from firebase (*.ts): this.list1= this.db.list("list1").valueChanges(); this.list2= this.db.list("list2 ...

Can you explain the purpose of this TypeScript code snippet? It declares a variable testOptions that can only be assigned one of the values "Undecided," "Yes," or "No," with a default value of "Undecided."

const testOptions: "Undecided" | "Yes" | "No" = "Undecided"; Can you explain the significance of this code snippet in typescript? How would you classify the variable testOptions? Is testOptions considered an array, string, or another d ...

Tips for maintaining knowledge after redirecting to a new page

Building an app using Ionic 4 where I need to display vouchers from a database as images. Each image should act as a link to a details page showing more information about that specific voucher. However, I am struggling to figure out how to keep track of th ...

TypeScript and Angular: Error Encountered when Trying to Combine Two Arrays

I'm attempting to combine two arrays of the same type that are nested within a "parent" array. The end goal is to flatten the structure. Below is the code I have been using: ngOnInit() { this.Logs.getAllLogs() .subscribe(logs => { ...

Refresh the Angular component following updates to the store

Working with NGRX/Redux for the first time, I am puzzled by why my component fails to update after adding a new item to an array in the store. Within my main component, these properties are defined: productLines$: Observable<ProductionLine[]>; produ ...

The React state remains stagnant and does not receive any updates

Although there have been numerous questions on this topic before, each one seems to be unique and I haven't found a close match to my specific issue. In my scenario, I have a grid containing draggable ItemComponents. When an item is selected, additio ...

What is the process for accessing getBoundingClientRect within the Vue Composition API?

While I understand that in Vue we can access template refs to use getBoundingClientRect() with the options API like this: const rect = this.$refs.image.$el.getBoundingClientRect(); I am now attempting to achieve the same using template refs within the com ...

Applying REGEX on input text in React Native

I'm having trouble getting my regex function to work correctly, so I believe there might be an error in my code. Any assistance would be greatly appreciated. Here is the regex function I am using: let validatePlate = (plate) => { var re = /(^[A ...

Unable to load Angular 2 Tour of Heroes application due to Typescript issue

My Angular 2 Tour of Heroes app seems to be stuck on the "Loading..." screen and I can't seem to figure out why. The angular-cli isn't showing any errors either. I'm currently at part five of the tutorial and it's becoming quite frustra ...

Confirm that a new class has been assigned to an element

I'm having trouble creating a unit test for a Vue.js component where I need to check if a specific CSS class is added to the template. Below is the template code: <template> <div id="item-list" class="item-list"> <table id="item ...