Negated types - #29317
Closed
Wesley Wigham (weswigham) wants to merge 2 commits into
Closed
Conversation
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not complaining, but why'd this go away?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I accidentally fixed a bug where we duplicated the elaboration - this is as the same as the line above.
First take, without having tried this out:
I do have some reservations on the feature for these reasons. If you think about our features trying to satisfy convenience, intent, and safety, then I don't know if this appropriately weighs convenience and intent.
Uh oh!
There was an error while loading. Please reload this page.
not (object & SomeInterface)It works just dandy~
Uh oh!
There was an error while loading. Please reload this page.
What do you mean? Yesterday you mentioned that you couldn't assign an array to a
not PromiseLike<any>.Uh oh!
There was an error while loading. Please reload this page.
Nope, you can't, because you can trivially make a
so, if you go to the example, I just state that I explicitly return an array that isn't PromiseLike - that is
T[] & not PromiseLike<any>Uh oh!
There was an error while loading. Please reload this page.
Per offline feedback from Anders Hejlsberg (@ahejlsberg) I've changed from
~unary operator to anotkeyword type operator (and updated all the text in this PR thus far to match). It does read nicer.Can this be used as a way to restrict the potential type of unbounded types? For example,
number & not 0to allow any number except the literal0.This doesn't look very useful at first glance but it can be powerful on mapped types. For example,
[key in string and not keyof CSS.Properties<any>]in https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e836acc75a78cf0655b5dfdbe81d69fdd4d8a252/types/styled-components/index.d.ts#L16-L25 could allow the constrained index signature to not have to includeCSS.Properties<string | number>[keyof CSS.Properties<string | number>].Yep. That's a primary driver for 'em.
🤔
Uh oh!
There was an error while loading. Please reload this page.
Is there an outline of how
notinteracts with narrowing? Might we have something like?Is there a short example that demonstrates wanting a
notfor an object literal?Daniel Rosenwasser (@DanielRosenwasser) Do you mean something like
{ x: number }is not assignable tonot { x: boolean }because{ x : number} & { x: boolean }does not get reduced tonever.Meta question for Daniel Rosenwasser (@DanielRosenwasser): You say:
that seem like some internal principles the TS team have for designing features? Is there a public description of these? I think it would help feature proposals if external contributors could frame their suggestions with the same language used internally.
@Kovensky I'm not sure that will type-check. The type
not keyof Tincludes any type which isn't one of the keys, such asboolean. You might need:though I'm not sure what the semantics will be for
nottypes appearing in mapped type constraints.Uh oh!
There was an error while loading. Please reload this page.
In this PR no negated types are produced by control flow yet; however we've talked over it and negated types make the lib type facts PR elegant to implement, since we can skip using conditionals (which don't compose well) and just filter with intersections of negated types :D
Aye, a test case with an example I pulled from a related issue:
Right now they're quietly dropped (aside from filtering out mismatching concrete types), like
symbolfor the same reason. There's no non-generic concept to map them into. I have a test to that effect. The arbitrary index signatures PR fixes this and would allow an index signature of a, say,string & not "". With that in place mapped types work correctly with 'em, since the intersections trivially desugar to an index signature.Wesley Wigham (@weswigham) Thanks! And this PR is very cool :)
Re: the object literal example. Should that not be a case where EPC raises an error? I know it doesn't right now because there is no discriminant, but it probably should. Will using negation types become the canonical way of dealing with examples like this? If not, and assuming EPC does get fixed, are there many other use-cases for the special object literal relation.
Uh oh!
There was an error while loading. Please reload this page.
Even if excess property checking makes defining a type like
Distinctunneeded, you'd still need the relationship to allow a type likeDistinctto be satisfiable (with fresh object types) should one be used.Uh oh!
There was an error while loading. Please reload this page.
Good job so far, I delayed some projects to wait for this feature - for more than a year.
Do these identities hold?
T & (not T)isnever.T & (not U)isTwhenTandUare disjoint."2b" | (not "2b")isunknown, or generallyT | (not T)isunknown.Just wondering what it would take to fix #28131
Yes. More generally, when U extends T,
U & not Tis never.Yes.
More generally when U extends T,
T | not Uis justunknown(quite literally the opposite of the intersection intoneverrule). That's correct though I don't remember if I'd actually implemented this one yet - afaik we don't currently have any union identities that cause unions to "simplify" tounknownyet so I remember thinking about how it's best accomplished for a bit.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really need to use this symbol? It almost sounds like a joke, but this could affect memory footprint when bootstrapping the compiler since modern engines can avoid full UTF16 representations https://blog.mozilla.org/javascript/2014/07/21/slimmer-and-faster-javascript-strings-in-firefox/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A minification step will simply remove this line. The article suggests to minify the code to utilise the inline strings and also help with strings interning. I guess, v8 should not be so much different in this sense and also win from code minification.
Uh oh!
There was an error while loading. Please reload this page.
Wesley Wigham (@weswigham) Seems to work, right?
Not quite sure what should happen here:
Currently it doesn't reduce; maybe infer types should be disallowed under
not?Uh oh!
There was an error while loading. Please reload this page.
It doesn't reduce because we don't reduce reducible conditionals with
infer's in them right now. IMO, it certainly still makes sense to allow aninferinside anot, sincenot A extends not infer Bofc should inferAforB.Yep, you're right. I guess it could infer
U = not string?Love coming back to this PR about once or twice a year because of not being able to do something.
Is there a chance to get any update on the state of it?
Would support for negated types make type inference involving conditional types easier and/or more powerful?
This experiment is pretty old, so I'm going to close it to reduce the number of open PRs.
Nathan Shively-Sanders (@sandersn) Is that a "no" to negated types, or will the concept be tracked somewhere else now? If the latter, I'd like to know where so that I can subscribe to the new conversation.
Not "no", but discussion should happen on an issue, #4196 probably, instead of a PR. PRs go stale.
Uh oh!
There was an error while loading. Please reload this page.
(Though admittedly the proposal in the PR description is admittedly way more concrete and detailed than anything in that issue rn, and the blockers for this are less about the syntax and behavior and more about implementation and performance + language complexity concerns, all of which are more tied to this PR specifically than the original idea)
while I certainly can relate to wanting to keep the PR count down to a manageable number (we have the same problem over on insomnia), I would like to echo the above from Wesley Wigham (@weswigham). I've been following this conversation very closely for years, since it and #29729 are the two features I am the most interested in.
I guess at the end of the day as long as this PR isn't locked we can all keep talking here, but it'd be fantastic to have a better understanding of what needs to happen next for negated types to rise-the-ranks a little in the prioritization. I don't want to spam iteration plans (e.g. #49074) with my wants-and-needs... but I'm not sure what else to do at this point.
I hit the need for negated types on a weekly basis. Should I just drop those use-cases in here whenever I hit them?
Dimitri Mitropoulos (@dimitropoulos) #49220 addresses the scenario in #29729 in a different way.
Thanks for mentioning #49220. I didn't even know I had this problem till I read it. What I'm hearing is that I should follow future discussion of negated types in #4196? They'd be absolutely wonderful to have for stuff like "number without NaN".
Not<string, 'not me'>sindresorhus/type-fest#417Why was such a basic and important feature rejected for a type system? That's a pity!
Autumn-one It wasn't rejected, the issue is still open: #4196
As to why it's not implemented yet? Priorities, time constraints, language constraints, limited resources. Feel free to fork the TypeScript repo and try to implement it, you'll see that it's not an easy task.