Deducing data types from arrays containing both narrow and wide elements

const ElemText = { value: string };
const ElemData = { value: string, numId: number };
 
function combineElements(
  elementsText: Array<ElemText>,
  elementsData: Array<ElemData>
) {
  const combinedElements = [...elementsText, ...elementsData];
  // const items: ElemText[]
}

Why is combinedElements of type ElemText[] and not Array<ElemText | ElemData>? It appears that the specific details about the broader type (ElemData) are being disregarded.

Answer №1

Take a look at this scenario:

type ProductNew = { name: string };
type ProductExisting = { name: string, id: number };

type Inventory = ProductNew | ProductExisting
declare var inventory: Inventory

const item = inventory.name // only the name property can be accessed

Since both types have the common property name, you are allowed to access the name property.

Because we cannot determine if inventory contains an id or not. Allowing access to the id property in this case could lead to potential runtime errors.

Now let's revisit your initial example:

type ProductNew = { name: string };
type ProductExisting = { name: string, id: number };

function mergeProducts(
    newProducts: Array<ProductNew>,
    existingProducts: Array<ProductExisting>
) {
    const products =  [...newProducts, ...existingProducts];
}

In this case, products is a union of

Array<ProductNew> | Array<ProductExisting>
. The same rule applies here where only the name property is considered safe to access.

If you wish to be able to access the id property as well, you can use the following helper function:

type ProductNew = { name: string };
type ProductExisting = { name: string, id: number };

type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
    T extends any
    ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;

type StrictUnion<T> = StrictUnionHelper<T, T>


function mergeProducts(
    newProducts: Array<ProductNew>,
    existingProducts: Array<ProductExisting>
) {
    const mergedProducts: Array<StrictUnion<ProductNew | ProductExisting>> = [...newProducts, ...existingProducts];
    mergedProducts[0].name // okay
    mergedProducts[0].id // number | undefined
}

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

While validating in my Angular application, I encountered an error stating that no index signature with a parameter of type 'string' was found on type 'AbstractControl[]'

While trying to validate my Angular application, I encountered the following error: src/app/register/register.component.ts:45:39 - error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used ...

Testbed: Issue encountered: Unable to resolve all parameters for PriDateInput component

I am facing an issue while creating an Angular Component with the help of TestBed. The error message I receive is as follows: Error: Can't resolve all parameters for PriDateInput: (?). error properties: Object({ ngSyntaxError: true }) ...

TS & Angular: Unlocking the Power of Conditional Interfaces

My user component includes a variable called user, which can be either an Employee or a Student. In my HTML, I have an element {{ user.coure ?? user.department }} I'm encountering an issue in my HTML because some properties in the Employee interface ...

The parameters of a generic class in Typescript are customizable and

Currently working on programming an internal abstract class for a project, and I need it to be generic in order to make it extendable. The goal is to have my class named as if it were extending the T template, like Sample extends T, so that all parameters ...

Error: Unable to find provider for Store in Angular 9 AuthService Guard

I have been working on implementing an Auth guard with Angular 9, but I encountered an ERROR in the browser console: ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(SampleModule)[AuthGuard -> Store -> Store -> Store -& ...

Contrasting EventEmitter and Output Decorators in Angular-Cli

Is there a reason why EventEmitter and Output Decorator are meant to be used in conjunction with each other? I'm having trouble distinguishing between the two. If they are intended to work together, wouldn't it make more sense to have just one d ...

How to efficiently retrieve parent content from a child component

parent.html <ion-content class="project"> <ion-grid> <ion-row class="details"> <project [data]="data"></project>// this child component </ion-row> </ion-grid> </ion-content> project.ht ...

Standard layout for a project with equally important server and client components

We are in the process of developing an open-source library that will consist of a server-side component written in C# for Web API, meta-data extraction, DB operations, etc., and a client-side component written in TypeScript for UI development. Typically, ...

What determines the narrowing of a type when it is defined as a literal versus when it is returned from a function?

I'm really trying to wrap my head around why type narrowing isn't working in this scenario. Here's an example where name is successfully narrowed down: function getPath(name: string | null): "continue" | "halt" { if (n ...

Unable to set the opacity of rc-tooltip to 1

Despite customizing our tooltips with CSS, we are still experiencing an issue where the tooltips appear translucent rather than having an opacity of 1. This is evident in the image provided (with text visible in the background): https://i.sstatic.net/nlM ...

The feature of Nuxt 3's tsconfig path seems to be malfunctioning when accessed from the

Take a look at my file structure below -shared --foo.ts -web-ui (nuxt project) --pages --index.vue --index.ts --tsconfig.json This is the tsconfig for my nuxt setup. { // https://v3.nuxtjs.org/concepts/typescript "exte ...

Create a TypeScript interface for an object with hyphens as the property keys

I am interested in transforming { "controller-element": { } } into an interface. However, due to the presence of a - in controller-element, I am unable to directly create export interface IControllerResponse { controller-element: any; } ...

Can you guide me on how to specify the return type in NestJS for the Session User in a request?

async authenticated(@Req() request: Request) { const user = request.user return user } It is important for the 'user' variable to have the correct type globally. While working with express passport, I came across the following: decl ...

Solving the problem of cookieParser implementation in NestJS

Greetings! I have a question for you. Currently, I am working on developing a backend using NestJS, a Node.js framework. Everything is functioning smoothly except for some issues when it comes to hosting. I have created a new NestJS project and made some ...

What is the best way to arrange items by utilizing the Array index in JavaScript?

Currently, I am attempting to make the elements within this angular component cascade upon loading. The goal is to have them appear in a specific layout as shown in the accompanying image. I'm seeking guidance on how to write a function in the TypeSc ...

Can you explain to me the significance of `string[7]` in TypeScript?

As I was working in TypeScript today, I encountered a situation where I needed to type a field to a string array with a specific size. Despite knowing how to accomplish this in TS, my instincts from writing code in C led me to initially write the following ...

How can we effectively implement alert notifications for validating image sizes and formats prior to uploading?

code playground : https://codesandbox.io/s/quizzical-lamport-ql5ep I'm encountering an issue with the code provided in the CodeSandbox link. https://i.sstatic.net/xg3aK.png I've attempted to resolve this issue using various methods, but unfortu ...

The Freemode feature in SwiperJS is not functioning properly when used with React TypeScript

Having a slight issue with SwiperJS. Using version 10.1.0 and the following code : import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; export default function Discover() { return ( <> ...

"Null value is no longer associated with the object property once it has

What causes the type of y to change to string only after the destruction of the object? const obj: { x: string; y: string | null } = {} as any const { x, y } = obj // y is string now ...

What is the correct way to convert a base type value to its extended type in TypeScript?

Consider the TypeScript code block below: type URLEx = URL & { custom: string; }; const url = new URL("http://localhost:3000/foo/var"); const url_x: URLEx = { ...url, custom: "hello", }; console.log(url); // Output properti ...