The generic parameter fails to meet the specified constraint

When examining the code below, it becomes apparent that an error is expected:

The new javascript doesn't satisfy
.

declare type Type = {
  0: [C, D],
  1: [2, 2],
}

type C = { C: number }
type D = { D: number }

type FirstExtendsSecond<First extends Second, Second> = void;
type Test<T extends 0 | 1> = FirstExtendsSecond<Type[T][1], Type[T][0]>;

Interestingly, if we substitute [2, 2] on line three with [C, C], the error vanishes. This outcome raises questions about the compiler's ability to recognize this inconsistency.

Answer №1

Regrettably, TypeScript is limited in its ability to recognize when certain actions should be restricted. The type checker takes some shortcuts to verify constraints, resulting in situations where errors slip through undetected, as discussed in GitHub issues like microsoft/TypeScript#46076.

This deliberate decision contributes to the intentional unsoundness of TypeScript's type system, maintaining usability by avoiding a trade-off between false negatives and false positives or compromising performance for accuracy in compiler checks.

To explore more about TypeScript's complex relationship with soundness, refer to microsoft/TypeScript#9825, along with discussions on Stack Overflow regarding topics like Why are TypeScript arrays covariant? and TypeScript type narrowing seems to make incorrect assumption.


Here's an elaborated version of your code highlighting why an error might be expected:

type FirstExtendsSecond<X extends Y, Y> =
    [X] extends [Y] ? true : false; // ideally always true

In theory, FirstExtendsSecond<X, Y> with restrictions on X should only exist if X extends Y holds true. This results in a non-distributive conditional type check where X extends Y should unequivocally yield true, right?

Unfortunately, the reality is different, demonstrated by this example:

declare type Type = {
    0: [string, number],
    1: [number, string],
}        
type Test<T extends 0 | 1> =
    FirstExtendsSecond<Type[T][1], Type[T][0]>; // no error

type Test0 = Test<0> // unexpected false 😲
type Test1 = Test<1> // unexpected false 😲

The definition of Test lacks any errors, yet both Test<0> and Test<1> resolve to

false</code! Neither <code>string extends number
nor number extends string hold, so why does Test's formulation not fail?


This scenario can be understood through a comment in this thread within microsoft/TypeScript#46076. In determining compatibility between types, an intentionally unsound rule is applied as follows:

A type S is related to a type T[K] if S is related to C, where C represents the base constraint of T[K]

In the context mentioned above,

The type Type[T][1] relates to

Type[T][0]</code since the former is connected to the base constraint of the latter, which translates to <code>Type[0 | 1][0]</code, or <code>string | number
.

This lack of error is due to successful type checking when substituting T with its constraint 0 | 1:

type Test01 = Test<0 | 1> // evaluates as true

While the rule presents unsoundness concerns we've experienced, it serves practical purposes. As explained in a commentary linked to the relevant issue microsoft/TypeScript#27470:

This approach balances compatibility with older versions and avoids overwhelming complexity from generic types.

Although TypeScript has addressed aspects of soundness over time, pushing for changes based on instances like the one presented may likely get dismissed given how adjustments often risk disrupting existing codebases.

Access Playground link for full code demonstration

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

Error: The property 'process' cannot be read because it is not defined

Seeking help with a code issue Any advice on best practices would be greatly appreciated. Thank you! An error has occurred: TypeError: Cannot read property 'process' of undefined myComponent.ts ProcessInfo: any | false; showSaveItems = ...

Tips for creating a redirect to a specific page after clicking a link in an email using Angular

I've been working on implementing a feature in Angular where users can click on a link provided in an email and then get redirected to the respective page after authentication. I've tried a few different approaches, but none of them seem to be wo ...

Passing data and events between components in React

I'm currently working on developing a dashboard app that includes a basic AppBar and a drawer. I based my design on this Demo. https://codesandbox.io/s/nj3u0q?file=/demo.tsx In the Demo, the AppBar, Drawer, and Main content are all contained within ...

When running the test, the message "Unable to resolve all parameters for BackendService" is displayed

Upon executing the ng test command, the following error was displayed. This is my service specification: describe('BackendService', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [ { p ...

Exploring the attributes of optional features

Dealing with optional properties can be quite tedious. Consider the object test1 in TypeScript: interface Test { a?: { b?: { c?: { d?: string } } }; } const test1: Test = { a: { b: { c: { d: 'e' } } } }; Handling the absence of each proper ...

Compiling Typescript upon saving in Sublime Text 3

Seeking a solution: How can I get Sublime Text 3 to automatically compile Typescript code when I save it? Having to switch between the terminal and Sublime is getting tedious. Appreciate any advice, thank you! ...

Error: The specified 'supportedValuesOf' property is not present in the 'Intl' type

My current date-time library is dayJS and I am looking to display a comprehensive list of all available time zones. I attempted to utilize the Intl object for this purpose: Intl.supportedValuesOf("timeZone") However, I encountered a typescr ...

Ways to statically type a function that produces an array from 1 to n

I am working on creating a function that can generate an array of numbers ranging from 0 to n, while ensuring that the returned type matches a known array structure at compile time. const makeFoo = (n: number) => [...Array(n).keys()]; const foo1 = [0, 1 ...

Angular is reporting that the check-in component is nonexistent

I encountered an error in my Angular 8 application while working on a component. The error seems to be related to nested components within the main component. It appears that if the component is empty, the error will be shown, but if it's not null, th ...

Managing form input in Ionic2 components from external sources in Angular2

Hello there, I am currently facing an issue with managing form validation along with proper styling for nested forms. Here's what I'm aiming to achieve: I have a Page that displays Tabs components with four tabs. Each tab represents a separate @ ...

React Native is throwing a TypeError because it is encountering an undefined object

React Native is throwing an error claiming Undefined is not an object when it's clearly an object!! I'm confused about what's happening. Take a look at the code snippet below. Scroll down to the render() function. You'll see the follow ...

"Every time you run mat sort, it works flawlessly once before encountering an

I am having an issue with a dynamic mat table that includes sorting functionality. dataSource: MatTableDataSource<MemberList>; @Output() walkthroughData: EventEmitter<number> = new EventEmitter(); @ViewChild(MatSort, { static: true }) sort ...

Having trouble importing a file in TypeScript?

I needed to utilize a typescript function from another file, but I encountered an issue: I created a file called Module.ts with the following code snippet: export function CustomDirective(): ng.IDirective { var directive: ng.IDirective = <ng.IDire ...

Updating the view of an HTML page in Angular after a set period of time can be achieved by

I am facing an issue while trying to dynamically display a router link based on a certain condition. The goal is to show the routerLink name within the div section if a specific condition is met. Initially, when I check {{isNameAvailable}}, it returns fals ...

Encountered a problem when implementing flowbite in a project using NextJS and TypeScript

I recently added tailwind and flowbite to my NextJS project. After importing "flowbite" in the _app.tsx file, I encountered the following error message: ReferenceError: document is not defined at Object.366 (D:\shopflo\next-tailwin ...

What is the best approach to creating multiple dropdowns in ant-design with unique options for each?

It seems like I may be overlooking a simple solution here. Ant-Design dropdowns utilize an array of ItemProp objects to show the options, but this restricts me to having only one list of options. const choices: MenuProps['items'] = [ { label: ...

Understanding the significance of an exclamation point preceding a period

Recently, I came across this code snippet: fixture.componentInstance.dataSource!.data = []; I am intrigued by the syntax dataSource!.data and would like to understand its significance. While familiar with using a question mark (?) before a dot (.) as in ...

What are the reasons behind the compilation failure of the react-sortable-hoc basic example when using typescript?

Take a look at this sample code snippet extracted from the official react-sortable-hoc webpage. import React, {Component} from 'react'; ... // Code snippet goes here ... render(<SortableComponent/& ...

Troubleshooting Async Function compatibility between Express and NestJs

Initially, I set up a small express server to handle report generation and file writing tasks. var ssrs = require('mssql-ssrs'); var fs = require('fs'); const express = require('express') const app = express() const port = 30 ...

Why is Zod making every single one of my schema fields optional?

I am currently incorporating Zod into my Express, TypeScript, and Mongoose API project. However, I am facing type conflicts when attempting to validate user input against the user schema: Argument of type '{ firstName?: string; lastName?: string; pa ...