镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Conditional type is deferred because narrow-as-assertion on constrained type parameter has no effect #29939

Description

Sorry for the gratuitous title.

Code

type IsString<X> = [X] extends [string] ? true : false

function isString<X extends string, Y>(x: X, y: Y)  {
    if (typeof x === "string") {
        const a: IsString<typeof x> = true; // error: conditional type is deferred
    }
    if (typeof y === "string") {
        const b: IsString<typeof y> = true;
    }
}

Expected behavior:

The two to behave the same; probably, they should both be OK.

Actual behavior:

The type of x does not get narrowed by assertion, presumably because the assertion adds no information. Conditional type resolution ignores parameter constraints when attempting to resolve using the most restrictive instantiation, and therefore IsString<typeof x> does not resolve.

Essentially T extends string, and T & string where T is unconstrained, do not behave the same in conditional check types. The former has its constraint ignored, while the latter gets to use its intersection proof.

Playground Link: link

Related Issues:

Activity

  1. changed the title [-]Conditional type is deferred because narrow-as-assertion on constrained type parameter has no affect[/-] [+]Conditional type is deferred because narrow-as-assertion on constrained type parameter has no effect[/+] on Feb 16, 2019
  2. jack-williams commented on Feb 19, 2019

    @jack-williams
    CollaboratorAuthor

    I understand why type parameter constraints are ignored by the restrictive instantiation. Using transitive reasoning with any breaks a whole load of things, as shown from the example in checker.ts.

    type Foo<T extends { x: any }> = T extends { x: string } ? string : number

    A question I have is: are there other examples that do not involve any, where ignoring constraints is necessary? I can think of another one:

    type Bar<T extends Object> = T extends object ? string : number

    Would it be fair to say that the restrictive instantiation is currently necessary purely because conditional type resolution uses assignability, a non-transitive relation?

    I know that definitelyAssignable was a thing at one point; was that crafted with the intent to be a transitive type relation?

  3. jack-williams commented on Aug 28, 2019

    @jack-williams
    CollaboratorAuthor

    I think I know enough about this now to say that it's a design limitation.

    • It is correct that a type parameter T extends string does not get narrowed to T & string upon typeof because the intersection adds no information: the type parameter is already a subtype of string.
    • The only other vector of change would be to consider constraints in conditional types but there are multiple reasons why this is difficult (and incorrect in some cases).

    This leads me to the conclusion that this is just an awkward composition of concepts, but one that few people seem to face.

  4. added
    Design LimitationConstraints of the existing architecture prevent this from being fixed
    and removed
    Needs InvestigationThis issue needs a team member to investigate its status.
    on Aug 28, 2019
  5. newlukai commented on Oct 8, 2020

    @newlukai

    I think I know enough about this now to say that it's a design limitation.

    * It is correct that a type parameter `T extends string` does not get narrowed to `T & string` upon `typeof` because the intersection adds no information: the type parameter is already a subtype of `string`.
    
    * The only other vector of change would be to consider constraints in conditional types but there are multiple reasons why this is difficult (and incorrect in some cases).
    

    This leads me to the conclusion that this is just an awkward composition of concepts, but one that few people seem to face.

    Jack Williams (@jack-williams) Would be really great to learn something here. Currently I'm trying to use a conditional type as return type of a function. Inside the function I'm narrowing down on the constraints of the conditional type to return the corresponding value (similar to this SO post.

    As I read your posts here I'm wondering about the square bracket notation in your IsString type. What does it do?

    And you wrote considering constraints in conditional types would sometimes be incorrect. I assume this is the reason why writing a function with conditional return type is not possible. Right? And could you explain why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions