Skip to content

Problem with function types, <T>() => T extends X ? () : ()Β #56721

Open

Description

@gs18004

πŸ”Ž Search Terms

"function", "generic function"

πŸ•— Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play?#code/C4TwDgpgBAYgdgHgBoD4oF4oICooBQCUGa2UEAHsBHACYDOUSUA-FAIxQBcUATAFChIUAKIBHAK4BDADZ02yADRQAmmnR8osRKjKVq9DVoSqWUYACdx0bgDMZdCHwHhoAdXMB7OAHMAShDpxaWAMEQl7eQBvcwhJGi9pEChJbgByAEFUgF8lSJSoDOyUJydBaHh5HUwcfCJ0El0qWgYmVg5ufjKtHmQ1LFxCYihSCib6RlN23mchMSlZHqQlE3VNCt7G-TpDeB6TVgsrLig7WUcZ6ABhD3MYgGNgf0Dg0Ln7HujY+LhE5LTMnJQPL-IpAA

πŸ’» Code

type Fn<X> = <T>() => T extends X ? 1 : 2
type Equals<X, Y> =
  Fn<X> extends
  Fn<Y> ? true : false

type A = Equals<{readonly a: 'A'}, {a: 'A'}>

πŸ™ Actual behavior

type A should shows true.

πŸ™‚ Expected behavior

type A should be false.

It works well when the code is like this.

type Fn1<X> = <T>() => T extends X ? 1 : 2
type Fn2<X> = <T>() => T extends X ? 1 : 2
type Equals<X, Y> =
  Fn1<X> extends
  Fn2<Y> ? true : false

type A = Equals<{readonly a: 'A'}, {a: 'A'}>

Additional information about the issue

No response

Activity

  1. RyanCavanaugh commented on Dec 11, 2023

    @RyanCavanaugh
    Member

    Where are people getting this definition of Equals from??

  2. jcalz commented on Dec 11, 2023

    @jcalz
    Contributor

    Probably originally from #27024 (comment) and then from sindresorhus/type-fest after that?

  3. fatcerberus commented on Dec 11, 2023

    @fatcerberus

    Context: This variant allows for distinguishing any from other types, which the more traditional variants of Equals don’t.

  4. gs18004 commented on Dec 12, 2023

    @gs18004
    Author

    Where are people getting this definition of Equals from??

    I got it from https://github.com/type-challenges/type-challenges/blob/main/utils/index.d.ts

  5. craigphicks commented on Dec 13, 2023

    @craigphicks

    @fatcerebus

    Context: This variant allows for distinguishing any from other types, which the more traditional variants of Equals don’t.

    What is amounts to is that the "correct" answer(s) to a type function "Equals(A,B)" depends on what it is being used for.

    In particular, the types any, never, unknown, and types described by an intersection & operator are corner cases that make Equals hard to define as a one-size-fits-all.

    Therefore exporting these intrinsic type functions

    • isAnyType
    • isNeverType
    • isUnknownType
    • isIntersectionType
    • getIntersectionTypes

    would enable the users to handle the corner cases as they need, and simply apply extends to the remainder.

  6. fatcerberus commented on Dec 13, 2023

    @fatcerberus

    Craig P Hicks (@craigphicks) That’s all true, but TS doesn’t add new pack-in utility types (intrinsic or otherwise) unless needed for declaration emit, so we have to work with what we have.

  7. craigphicks commented on Dec 14, 2023

    @craigphicks

    Craig P Hicks (@craigphicks) That’s all true, but TS doesn’t add new pack-in utility types (intrinsic or otherwise) unless needed for declaration emit, so we have to work with what we have.

    If that in order not to pollute the global namespace? That's legit, but using a namespace (e.g. ts.) to prefix all utilities would solve it.

  8. RyanCavanaugh commented on Dec 14, 2023

    @RyanCavanaugh
    Member

    The reason for not adding utility types is that whatever definition we choose will invariably not match what 30% (minimum) of people expect to happen w.r.t how they treat any, whether they distribute over unions, how they treat unknown/void, whether the mapped forms are homomorphic, etc, leading to unending complaints that we picked the "wrong" definition of any given utility type.

  9. added
    Cursed?It's likely this is extremely difficult to fix without making something else much, much worse
    Possible ImprovementThe current behavior isn't wrong, but it's possible to see that it might be better in some cases
    on Dec 14, 2023
  10. RyanCavanaugh commented on Dec 14, 2023

    @RyanCavanaugh
    Member

    It seems like we're using the strict subtype relation in one case, and regular assignability in the other. I don't even know which people are wanting with this definition of Equals! It's perhaps possible to unify to the same relation but it's very difficult to predict what would break in that case.

    I'd really encourage everyone to just use something like

    type Equals<X, Y> = [X] extends [Y] ? [Y] extends [X] ? true: false;

    if they're not actively trying to probe the strangest corners of the type system πŸ˜΅β€πŸ’«

  11. fatcerberus commented on Dec 15, 2023

    @fatcerberus

    I don't even know which [type relation] people are wanting with this definition of Equals

    The intent of this definition of Equals is ultimately to do isTypeIdenticalTo(X, Y); I believe the correct path for that is assignability (specifically, of deferred conditional types)

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

    Cursed?It's likely this is extremely difficult to fix without making something else much, much worseDomain: check: Variance RelationshipsThe issue relates to variance relationships between typesHelp WantedYou can do thisPossible ImprovementThe current behavior isn't wrong, but it's possible to see that it might be better in some cases

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions