What is the best way to create an interface tailored to a specific scenario using TypeScript?

In my TypeScript project without React, I am dealing with an interface structured like this:

export interface LayerStyling<T> {
    attribute: string;
    value: AllowedTypes;
    type: LayerTypes;
    layout?: {
        icon: string
    };
    state: {
        [key in States]: ObjectType<T>
    }
}

In this interface, the possible values for LayerTypes are "default" or "icon", and the presence of the "layout" key depends on the type selected.

If LayerTypes is set to "default", the interface will look like this:

export interface LayerStyling<T> {
    attribute: string;
    value: AllowedTypes;
    type: LayerTypes;
    state: {
        [key in States]: ObjectType<T>
    }
}

If LayerTypes is set to "icon", the interface will be slightly different:

export interface LayerStyling<T> {
    attribute: string;
    value: AllowedTypes;
    type: LayerTypes;
    layout: {
        icon: string
    };
    state: {
        [key in States]: ObjectType<T>
    }
}

The presence of the "layout" key is determined by the "LayerTypes" value.

I encountered an issue while trying to access the "icon" property using:

const { layout: { icon } } = attrs; // It throws an error Property 'icon' does not exist on type '{ icon: string; } | undefined'.

I seek guidance on how to resolve this situation. Thank you!

Answer №1

If I were to tackle this, I would follow these steps:

declare type LayerTypes = 'default' | 'icon';
declare type AllowedTypes = string;
declare type States = 'a' | 'b';
interface ObjectType<T> {

}

export interface LayerStyling<T> {
  attribute: string;
  value: AllowedTypes;
  state: {
    [key in States]: ObjectType<T>
  };
  // type: LayerTypes; // optional
}

export interface LayerStylingDefault<T> extends LayerStyling<T> {
  type: 'default';
}

export interface LayerStylingIcon<T> extends LayerStyling<T> {
  type: 'icon';
  layout: {
    icon: string;
  };
}

const customIcon: LayerStylingIcon<number> = {
  attribute: '',
  value: '',
  state: {a: '', b: ''},
  type: 'icon',
  layout: {
    icon: '',
  },
};

const customDefault: LayerStylingDefault<number> = {
  attribute: '',
  value: '',
  state: {a: '', b: ''},
  type: 'default',
};

// Alternative syntax
export type LayerStylingDefault2<T> = LayerStyling<T> & {type: 'default'};
export type LayerStylingIcon2<T> = LayerStyling<T> & {
  type: 'icon';
  layout: {
    icon: string;
  };
};

const customDefault2: LayerStylingDefault2<number> = customDefault;
const customIcon2: LayerStylingIcon2<number> = customIcon;

// To create type guards, you can refer to:
// https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards
function isLayerStylingIcon(e: LayerStylingDefault<any> | LayerStylingIcon<any>): e is LayerStylingIcon<any> {
  return e.type === 'icon';
}

Answer №2

The error message you are receiving is correct because the icon field is optional. When de-structuring LayerStyling, be sure to account for this.

To handle this, you can use the following approach:

// The type of icon will be string or undefined
const { layout: { icon } = {} } = attr

if (icon !== undefined) {
  console.log('The LayerTypes was icon')
} else {
  console.log('The LayerTypes was default')
}

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

Having difficulty subscribing to multiple observables simultaneously using withLatestFrom

I am facing an issue where I have three observables and need to pass their values to a service as parameters. I attempted to do this using WithLatestFrom(), but it works fine only when all values are observables. this.payment$.pipe( withLatestFrom(this.fir ...

Creating type definitions for TypeScript (ts) involves defining the shape and

Having trouble using a library installed in node_modules/ within a typescript app? Here's a quick hack for you. Create a folder named after the module in typings/modules and add an index.d.ts file inside with the following content: declare module "li ...

Combining the namespace and variable declarations into a single statement

Currently, I am facing an issue with creating a declaration file for the third-party library called node-tap. The main challenge lies in properly declaring types for the library. // node_modules/a/index.js function A() { /* ... */ } module.exports = new A ...

Having trouble importing the hash-set module in TypeScript/SystemJS?

In the midst of developing an Aurelia project with TypeScript to generate JavaScript, I decided to incorporate another custom library called 'hash-set' (installed using jspm install npm:hash-set --save). However, I encountered difficulties in act ...

Angular: Implementing conditional HTTP requests within a loop

Currently, I am facing a challenge where I need to loop through an array of objects, check a specific property of each object, and if it meets certain criteria, make an HTTP request to fetch additional data for that object. The code snippet below represen ...

Angular Project: Exploring Classes and Interfaces with and without the Export Keyword

Currently, I am delving into the world of Angular. I have taken up a video course and also referred to a PDF book, but I find myself perplexed when it comes to understanding the usage of the "export" keyword... The PDF course focuses on Angular 5 and util ...

Declaring a function in TypeScript triggers an error notification

I encountered a typescript error while running my code stating that "require" is not a function. To resolve this, I declared the function beforehand; however, a new typescript error now occurs saying "Modifiers cannot appear here." Can anyone assist me w ...

Retain annotations for assigned types in d.ts files

When compiling declarations for the code snippet below using Typescript v5.0.4, I encounter an issue: type SomeType<T> = { field: T; }; const getInstance = <T>( value: T ): SomeType<{ [K in keyof T]: T[K]; }> => { return { ...

What is the most effective way to eliminate all values in an object key array except for one specific value?

Currently, I am developing an angular project and have encountered an object with the following structure: const obj = { fruits: ['apple', 'orange', 'None'], nation: ['usa'], city: ['New York', ' ...

Creating an array of strings using data from a separate array of objects in JavaScript/TypeScript

I want to generate a new array of strings based on an existing array of objects, with each object belonging to the Employee class and having a firstName field: assignees: Array<Employee>; options: string[]; I attempted to achieve this using the fol ...

"Exploring the incredible powers of Ionic2, Angular2, HTTP requests, and

Despite all the research I've done on observables, I still struggle to grasp how they function. The HTTP request code snippet is as follows: import { Component, OnInit, Injectable } from '@angular/core'; import { Http, Response, Headers, R ...

How can we ensure that only one of two props is specified during compilation?

I've designed a customized Button component. interface Button { href?: string; action(): void; } I'm looking to ensure that when a consumer uses this Button, they can only pass either href or action as a prop, not both. I want TypeScri ...

How to efficiently mock the $window object in Angular unit tests

Attempting to unit test an angular custom service written in Typescript has been a challenge for me. The service is designed to read a global variable defined on the Window object and I have made it promise-based for potential future AJAX calls. Below is a ...

Since updating from Angular 16 to 17, I am experiencing a TypeScript compilation issue specifically related to 'openui5'

Everything was running smoothly in Angular16. I had "@types/openui5" : "1.40.4" listed in my dev-dependencies. Here is how it's configured in the tsconfig.json: { "compilerOptions": { "downlevelIteration": ...

The source code in VS Code was not accurately linked

I'm currently facing an issue with running my angular2 project from vs code. Below are the contents of my tsconfig and launch.json files. tsconfig.json { "compilerOptions": { "declaration": false, "emitDecoratorMetadata": true, "experi ...

Tips on verifying the presence of a child in a Firebase list using AngularFire2

I am working on checking the existence of a Firebase list using AngularFire2 in Angular. Below is my parent list: https://i.sstatic.net/gSjEb.jpg I specifically need to verify if the child element Messages exists within this list. This is the code snip ...

What is the significance of the colon before the params list in Typescript?

Consider the following code snippet: import React, { FC } from "react"; type GreetingProps = { name: string; } const Greeting:FC<GreetingProps> = ({ name }) => { // name is string! return <h1>Hello {name}</h1> }; Wha ...

Enhance a subject's behavior by overriding the .next method using a decorator

Currently, I am working on an Angular application where I have numerous Subjects, BehaviorSubjects, and ReplaySubjects as properties in various services. I am attempting to create a TypeScript decorator that can be added to some of these Subjects to enhanc ...

Creating a conditional class property requirement in TypeScriptAnother way to implement conditionally required class

I am currently in the process of creating an Event class with a data property that is typed based on the type of the event. Here is the code snippet I have been working on: export interface EventTypes { localeChange: { locale: string; }; translat ...

Can we establish communication between the backend and frontend in React JS by utilizing localstorage?

Trying to implement affiliate functionality on my eCommerce platform. The idea is that users who generate links will receive a commission if someone makes a purchase through those links. However, the challenge I'm facing is that I can't store the ...