What causes two identical generic types to produce varying results?

Can you help me understand the logic behind this TS compiler situation? I'm having trouble distinguishing between LookUp and LookUpWrong, and I can't comprehend why LookUpWrong always results in never.

type LookUp<U, T extends string> = U extends { type: string } ? U["type"] extends T ? U : never : never
type LookUpWrong<U extends { type: string }, T extends string> = U["type"] extends T ? U : never

type Animal = { type: 'dog' } | { type: 'cat' }

type ValidResult = LookUp<Animal, 'dog'> // { type: 'dog' }
type WrongResult = LookUpWrong<Animal, 'dog'> // never

Answer №1

Although my knowledge of typescript is limited, I decided to give it a try in the playground following @jcalz's input.

Upon exploring, I noticed that:

type LookUp<U, T extends string> = U extends { type: string } ? U["type"] extends T ? U : never : never
type LookUpWrong<U extends { type: string }, T extends string> = U extends { type: string }? U["type"] extends T ? U : never : never

type Animal = { type: 'dog' } | { type: 'cat' }
type Banimal = { type : 6};

type ValidResultLookUp = LookUp<Animal, 'dog'> // { type: 'dog' }
type ValidResult2LookUpWrong = LookUpWrong<Animal, 'dog'> // {type:'dog'}

const obj:ValidResultLookUp = {type:'dog'};
const obj2:ValidResult2LookUpWrong = {type:'dog'};;

type invalidResultLookUp = LookUp<Banimal,'dog'> // doesn't give error since it's just U which Banimal satisfies
type invalidResultLookUpWrong = LookUpWrong<Banimal,'dog'> // gives error before hand since Banimal doesn't extend {type:string}

If you reformat LookUpWrong to match LookUp with the additional conditional check U extends {type:string}, both types work as expected when used to construct ValidResultLookUp and ValidResult2LookUpWrong. This is because the Animal type fulfills the {type:string} requirement of the condition. Introducing this conditional check in LookUpWrong activates the distributive conditional types property, changing the behavior as follows:

type someType = 'dog' | 'cat' extends 'dog' ? 'desiredType' : never;

someType will now be never

Instead, we see the following:

type someType = 'dog' extends 'dog' ? 'desiredType' : never;
The presence of cat is irrelevant here as dog extending itself due to distribution results in a truthy condition and yields desiredType.

Consider the case of Banimal, where {type:6}. Using LookUp does not trigger an error when creating invalidResultLookUp because U does not adhere to

{type:string}</code unlike <code>LookUpWrong
(in the generics section).

Hence, the conditional check (for distributive conditional effect) is essential in LookUpWrong, preventing incorrect type assignments while building other types due to the added U extends {type:string} safeguard.

Explore the playground through this link.

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

The Angular Router is continuing to show the HomeComponent upon navigation, rather than just displaying the ChildComponent

Lately, I've been diving into Angular and attempting to create a github search application using the github api. However, I've encountered some roadblocks with routing and data passing. My goal is for the user to land on a page like /user/userID ...

Proper application of this - encountering issues with property emit of undefined in Angular

I am working with an angular component and encountering the following code: @Output() fixPercentChanged = new EventEmitter<number>(); In addition, I have this event: fixChanged(e) { setTimeout(() => { let fix = e.component.option(&apo ...

"NODEJS: Exploring the Concept of Key-Value Pairs in Object

I am facing a challenge with accessing nested key/value pairs in an object received through a webhook. The object in req.body looks like this: {"appId":"7HPEPVBTZGDCP","merchants":{"6RDH804A896K1":[{"objectId&qu ...

A mistake occurred during the afterAll function, resulting in a TypeError: Unable to access properties of an undefined entity (specifically, trying to read '

While creating my spec file and settings, I encountered an error in the console: 'An error was thrown in afterAll TypeError: Cannot read properties of undefined (reading 'toLowerCase')', What could be causing this error to appear? H ...

Angular progress bar with dynamic behavior during asynchronous start and stop

Currently, I am facing an issue with the progress bar functionality while utilizing the ng-bootstrap module. The scenario involves a dropdown menu with multiple options, and my desired behavior includes: The ability to start/stop the progress bar indepen ...

A guide to simulating Custom Dialog in Angular for Unit Testing with Jest

Currently, I am in the process of creating test cases for unit tests and ensuring code coverage for a method that triggers a dialog component when isEdit = 'true' which is retrieved from localStorage. The issue arises with the first test case wh ...

The parameter type must be a string, but the argument can be a string, an array of strings, a ParsedQs object, or an array of ParsedQs objects

Still learning when it comes to handling errors. I encountered a (Type 'undefined' is not assignable to type 'string') error in my code Update: I added the entire page of code for better understanding of the issue. type AuthClient = C ...

Input tag paired with a date picker

Instead of using the <label>{{list.createdat | date: 'dd.MM.yyyy'}}</label> tag, I want to change it to an input tag to create a simple datepicker: <input type="date"/> The issue is that the data in the <label> tag is pa ...

The variable 'myForm' is missing an initializer in the constructor and has not been explicitly assigned a value

import { Component, HostListener, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-contact-form', templateUrl: './contact-form.compon ...

The Angular material slider experiences issues with functionality when paired with the *ngFor directive

Having a unique problem that I could easily replicate on stackblitz. When using multiple mat sliders generated from a *ngFor loop with numbers as values, encountering an issue where moving the first slider affects all others. Subsequent drags only update ...

When initialized within an object, Angular may identify a field as undefined

Whenever I attempt to access a property of the object named "User," it shows up as undefined. However, upon logging the complete object to the console, the field appears with the necessary data. Here is the console log output: perfil.component.ts:42 unde ...

Exporting Arrays in Ionic Angular with Typescript is an essential feature

When trying to access an exported component in Typescript, there may be issues with importing the module correctly into another component. An error message is displayed when calling the AddToArray method: Cannot read property 'push' of undefi ...

Angular/Typescript ESLint rule for enforcing pure functions

Are there specific ESLint rules to ensure that only pure functions are written in TypeScript? I am looking to strictly write only pure functions in my code. Could someone please provide guidance on how to accomplish this? Appreciate any help in advance ...

Why is it important to use linting for JavaScript and TypeScript applications?

Despite searching extensively on Stack Overflow, I have yet to find a comprehensive answer regarding the benefits of linting applications in Typescript and Javascript. Any insights or resources would be greatly appreciated. Linting has become second natur ...

Manipulating Angular and Typescript to utilize the method's parameter value as a JavaScript object's value

I am currently working with Ionic, Angular, and Typescript, attempting to dynamically set the value of a location based on the parameter passed from a method. Here is the relevant code snippet: async fileWrite(location) { try { const result = a ...

Authenticating to Google APIs using Node.js within a lambda function: A step-by-step guide

I'm encountering an issue when trying to connect a Google sheet to an AWS Lambda function. While the code runs smoothly during local testing, upon deployment to the function, I receive an error message indicating that the credentials.json file cannot ...

Managing Data Types in a React and Express Application

I am working on a project that includes both a React client and a Node-Express backend. Currently, my React app is running with TypeScript and I am looking to switch my backend to TypeScript as well. At the moment, my project structure consists of a clien ...

Guide to leveraging clsx within nested components in React

I am currently using clsx within a React application and encountering an issue with how to utilize it when dealing with mappings and nested components. For instance: return ( <div> <button onClick={doSomething}>{isOpened ? <Component ...

Posting an array as form data in Angular Typescript: A step-by-step guide

Hello there, I'm currently developing an application using Angular 8 and integrating web.api within .net core 2.2. One of the challenges I encountered is dealing with multi-selectable checkboxes in a form that also includes "regular" inputs and file ...

Nuxt - asyncData ISSUE: "Variable '$axios' is inferred to have an 'any' type."

Referencing the guidelines provided at Encountering an error logged in console while executing yarn dev: ERROR ERROR in pages/index.vue:51:21 ...