Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
gs18004 opened this issue Dec 9, 2023 · 10 comments
Open

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

gs18004 opened this issue Dec 9, 2023 · 10 comments
Labels
Cursed? It's likely this is extremely difficult to fix without making something else much, much worse Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases

Comments

@gs18004
Copy link

gs18004 commented Dec 9, 2023

πŸ”Ž 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

@RyanCavanaugh
Copy link
Member

Where are people getting this definition of Equals from??

@jcalz
Copy link
Contributor

jcalz commented Dec 11, 2023

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

@fatcerberus
Copy link

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

@gs18004
Copy link
Author

gs18004 commented Dec 12, 2023

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

@craigphicks
Copy link

@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.

@fatcerberus
Copy link

@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.

@craigphicks
Copy link

craigphicks commented Dec 14, 2023

@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.

@RyanCavanaugh
Copy link
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.

@RyanCavanaugh RyanCavanaugh added Help Wanted You can do this Cursed? It's likely this is extremely difficult to fix without making something else much, much worse Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases labels Dec 14, 2023
@RyanCavanaugh
Copy link
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 πŸ˜΅β€πŸ’«

@fatcerberus
Copy link

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
Labels
Cursed? It's likely this is extremely difficult to fix without making something else much, much worse Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases
Projects
None yet
Development

No branches or pull requests

5 participants