Uh oh!
There was an error while loading. Please reload this page.
Add NoInfer<T> intrinsic represented as special substitution type - #56794
Conversation
Andrew Branch (andrewbranch)
commented
Dec 15, 2023
TypeScript Bot (@typescript-bot) pack this |
Heya Andrew Branch (@andrewbranch), I've started to run the tarball bundle task on this PR at b25bf3e. You can monitor the build here. |
Hey Andrew Branch (@andrewbranch), I've packed this into an installable tgz. You can install it for testing by referencing it in your and then running There is also a playground for this build and an npm module you can use via |
| Lowercase: IntrinsicTypeKind.Lowercase, | ||
| Capitalize: IntrinsicTypeKind.Capitalize, | ||
| Uncapitalize: IntrinsicTypeKind.Uncapitalize, | ||
| NoInfer: IntrinsicTypeKind.NoInfer, |
There was a problem hiding this comment.
Slightly related question: should ThisType become an intrinsic type as well (it is kinda intrinsic by nature but it's not defined as such in the typedefs)? Or is it better to not touch it at all?
There was a problem hiding this comment.
Obviously this shouldn't be touched as part of this PR - I just take this opportunity to ask a question about it ;p
There was a problem hiding this comment.
We want to leave ThisType as is. An intrinsic declaration doesn't really say anything about the internal representation of the type, and we already have a perfectly good solution for that with the current interface-based declaration.
Anders Hejlsberg (ahejlsberg)
commented
Dec 15, 2023
TypeScript Bot (@typescript-bot) test top100 |
Heya Anders Hejlsberg (@ahejlsberg), I've started to run the tsc-only perf test suite on this PR at b25bf3e. You can monitor the build here. Update: The results are in! |
Heya Anders Hejlsberg (@ahejlsberg), I've started to run the diff-based user code test suite on this PR at b25bf3e. You can monitor the build here. Update: The results are in! |
Heya Anders Hejlsberg (@ahejlsberg), I've started to run the diff-based top-repos suite on this PR at b25bf3e. You can monitor the build here. Update: The results are in! |
Heya Anders Hejlsberg (@ahejlsberg), I've started to run the parallelized Definitely Typed test suite on this PR at b25bf3e. You can monitor the build here. Update: The results are in! |
TypeScript Bot (typescript-bot)
commented
Dec 15, 2023
Anders Hejlsberg (@ahejlsberg) Here they are:CompilerComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TypeScript Bot (typescript-bot)
commented
Dec 15, 2023
Anders Hejlsberg (@ahejlsberg) Here are the results of running the user test suite comparing There were infrastructure failures potentially unrelated to your change:
Otherwise... Something interesting changed - please have a look. Details
|
TypeScript Bot (typescript-bot)
commented
Dec 15, 2023
Hey Anders Hejlsberg (@ahejlsberg), the results of running the DT tests are ready. |
Bruce Pascoe (fatcerberus)
commented
Dec 15, 2023
Huh, so it does... here I had thought TS never inferred cross-candidate unions, but apparently there are cases when it does? |
Ryan Cavanaugh (RyanCavanaugh)
commented
Dec 15, 2023
Unions can be formed under the same constrained primitive family. That's why my TS Congress talk had to disavow knowledge of primitive types. |
TypeScript Bot (typescript-bot)
commented
Dec 15, 2023
Anders Hejlsberg (@ahejlsberg) Here are the results of running the top-repos suite comparing Everything looks good! |
Anders Hejlsberg (ahejlsberg)
commented
Dec 16, 2023
Tests and performance all look unaffected. |
All these cases can be addressed by using It is not a trick. Do you have any stronger cases? I would not call those test cases buggy. With only that evidence it seems like perhaps this pull is a performance optimization, because you are explicitly shutting down inference, which could only shorten processing time, which is not a bad thing at all. |
I have been summoned to summon TypeScript Bot. TypeScript Bot (@typescript-bot) pack this |
Heya Daniel Rosenwasser (@DanielRosenwasser), I've started to run the tarball bundle task on this PR at e492451. You can monitor the build here. |
Hey Daniel Rosenwasser (@DanielRosenwasser), I've packed this into an installable tgz. You can install it for testing by referencing it in your and then running There is also a playground for this build and an npm module you can use via |
| if (type.flags & TypeFlags.Substitution) { | ||
| return typeToTypeNodeHelper((type as SubstitutionType).baseType, context); | ||
| const typeNode = typeToTypeNodeHelper((type as SubstitutionType).baseType, context); | ||
| return isNoInferType(type) ? factory.createTypeReferenceNode("NoInfer", [typeNode]) : typeNode; |
There was a problem hiding this comment.
What happens in declaration emit when something like this gets serialized?
// foo.tsexportconstf: <T>(x: T,y: NoInfer<T>)=>bool;// bar.tsimport{f}from"./foo.js";typeNoInfer<T>=number;exportconstg=f;There was a problem hiding this comment.
With the latest commit this will now emit globalThis.NoInfer<T>, similar to what we do for other global types in conflict situations.
There was a problem hiding this comment.
Do we have a test for that? Same with Uppercase etc.
There was a problem hiding this comment.
We don't, at least not that I can tell.
There was a problem hiding this comment.
Test added.
My gut feeling is that the I have to explain what I mean by "belongs to the call". You might have a multiple parameter types that appears in multiple calls: Trying to infer In order to infer Switching to member The ability to reach into type structure to infer type from specified members could be useful, but NoInfer types do not make a difference to what is currently allowed. (I tried). And if it did, the choice of which member to infer from is likely depend upon the function, not the parameter type passed. |
Cases from 2.8 release documentation + NoInfer Try with predefined type |
Anders Hejlsberg (ahejlsberg)
commented
Dec 20, 2023
TypeScript Bot (@typescript-bot) pack this |
Heya Anders Hejlsberg (@ahejlsberg), I've started to run the tarball bundle task on this PR at 2fc975a. You can monitor the build here. |
Hey Anders Hejlsberg (@ahejlsberg), I've packed this into an installable tgz. You can install it for testing by referencing it in your and then running There is also a playground for this build and an npm module you can use via |
Flavien Volken (Xample)
commented
Feb 20, 2024
I'm surprised not to see more examples where the type is infered from the returned value. Here is a (simplified) real world example of a code I faced where it was unpossible to catch errors without the // TS <5.4functiongetValue<T=string>(): T{returnundefinedasT;}functiongetNumberInline(): number{returngetValue();// no error, T in inferred as number (weird behavior)}functiongetNumberUsingVariable(): number{constvalue=getValue();// error, T takes the default string valuereturnvalue;}// TS >=5.4functiongetValueNoInfer<T=string>(): NoInfer<T>{returnundefinedasT;}functiongetNumberInlineNoInfer(): number{returngetValueNoInfer();// error, T takes the default string value}functiongetNumberUsingVariableNoInfer(): number{constvalue=getValueNoInfer();// error, T takes the default string valuereturnvalue;} |
Dmitriy Verenich (dhmk083)
commented
Feb 24, 2024
It would be nice if the following pattern could work: declarefunctioncreate<T>(fn: (get: ()=>NoInfer<T>)=>T): Tcreate(get=>({id: 123,getId(){returnget().id}}))Right now If I understand |
Flavien Volken (Xample)
commented
Feb 24, 2024
Dmitriy Verenich (@dhmk083) shouldn't you write: declarefunctioncreate<T>(fn: (get: ()=>NoInfer<T>)=>T): Tconstget=()=>({id: 123,getId(){returnget().id}})create(get) |
Dmitriy Verenich (dhmk083)
commented
Feb 24, 2024
No, it was just a simplified example. Here you can read about real world usage. (Check the first collapsible section |
Dmitriy Verenich (@dhmk083) currently, TS can't defer this sufficiently enough, it has to assign the contextual type for It's probably not impossible to improve this by deferring the instantiation of those parameter types that are only observable by the return types. I don't feel that it's particularly related to And note that ur |
This PR adds official support for a
NoInfer<T>marker type that blocks inferences to the contained type. The PR supersedes #52968, but borrows as much as possible from that PR (lots of thanks for your work, Mateusz Burzyński (@Andarist)).The PR represents
NoInfer<T>types as special substitution types (types with kindTypeFlags.Substitution) having constraint typeunknown, a pattern that otherwise never occurs. This representation ensures that we maximally leverage our existing infrastructure for erasing marker types when appropriate, and that we minimally disrupt the tooling ecosystem (because we're not introducing a new kind of type).Other than blocking inference,
NoInfer<T>markers have no effect onT. Indeed,TandNoInfer<T>are considered identical types in all other contexts.Some examples:
Above, inferences to the type of
bare blocked by theNoInfer<T>marker, thus causing errors to be reported when the argument types differ. Without theNoInfer<T>markers, inference simply produces the union"foo" | "bar".Fixes#14829.