You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Assertion functions don't work when defined as methods #36931
Assertion functions always need explicit annotations. You can write this:
let azzert: Assertion['assert'] = new Assertion().assert;
Reacted by S. Tarık Çetin, Steven Luscher and Pavel Kabir
Reacted by Kael Lumen and Anton Mikhailov
Reacted by Daniel M., Sean Sellek, Harminder Virk, pstrh, Chris Krycho, Gordey Doronin, Ivan Vanderbyl, Luis Pais, Leandro Aguiar, Daniel Garcia and 24 more
How does the exists function not have an explicit annotation? Or what can I do to resolve it in this case.
If I just export the function as non-default and do the import as import { exists } from "./assert" then it works but I'd like to have them grouped on the object assert.xyz.
Create a let/const/var and assign it the assert function you imported. Give that variable an explicit type like in the example provided by Ryan Cavanaugh (@RyanCavanaugh) .
Assertion functions always need explicit annotations
But I believe in my code the function does have an explicit type annotation, yet its not working. Are we saying there is a bug and that the variable assigned to the function needs the explicit type as well?
So when you say the type needs to be explicit its boggling my mind because I don't get how it couldn't be explicit.
But now, I'm wondering if you don't mean the function's type but the type of the argument needs to be explicit? As in the value: unknown is the issue? It needs to be non-generic and not something such as any or unknown? Is that what you mean?
Reacted by reverofevil, David Gilbertson, IronSpecs and Dennis Kats
Justin Chase (@justinmchase) I think the part that is boggling your mind is that the message isn't clear about what exactly doesn't have an explicit type. You quoted this code, which clearly does have an explicit type:
export function exists(value: unknown): asserts value {
assert(value);
}
But when you used it, you imported it like this:
import assert from "./assert";
assert.exists(true); // error ts(2775)
The 2775 error you are getting on that line is not about exists needing an explicit type, it's about assert needing one. You can verify this by importing exists directly:
import { exists } from "./assert";
exists( true ); // works without the error.
So the error isn't telling you about problems with exists, it's telling you that your default export needs to be explicitly typed:
It isn't entirely clear to me why that is the case, and it seems that making the error message clearer about where exactly the problem lies was discussed and rejected.
What I have found can work to avoid having to repeat the typings twice is to move the actual assertion functions into a separate file and then re-export them:
export * from './real-assertions';
import * as assertions from './real-assertions';
export default assertions;
I'm also not entirely clear on why this works, but it seems like it does..
Reacted by Karol Majewski, Justin Chase, Daniel M., Marnus Weststrate, Chris Krycho, Matt Kantor, David Gilbertson, Pat Tullmann, Constantin Piber, Leandro Aguiar and 5 more
We discussed in the team room and agreed that the existing message is fine, so I didn't change it.
—seeing as it took me about half an hour to finally find my way to this thread and understand the problem, and another half an hour to figure out how to rewrite our DefinitelyTyped tests to continue handling this! 😅
For the record, if anyone else is curious, this is how I'm ending up working around it for the types PR I'll be pushing up shortly:
I tried to include an assertion function as part of the exports for an inner module in one of my libraries and was heartbroken to find that, while I could use it perfectly within the module, I could not use it when referencing it from outside via the module namespace. Ryan Cavanaugh (@RyanCavanaugh)'s answer was a godsend!! Thank you so much!
Here's what I was doing:
Project1::src/Http.ts
exportfunctionassertThing(thing: any): asserts thing is string{// ...}
Project1::src/index.ts
import*asHttpfrom"./Http";// ....export{Http,/* .... and other things .... */}
While a little awkward, this is a total life-saver and has allowed me to successfully use this wonderful assertion functionality without limitation. (Note that it was necessary to use the (typeof Http) notation because typescript complained that namespaces can't be used as types.)
Anyway, just wanted to add this for anyone who may be struggling with something similar. Thanks again for the tip, Ryan Cavanaugh (@RyanCavanaugh)!
scoped 'logging' utility that adds prefix to logs or asserts simply to save me keystrokes and improve dev-experience when debugging or analyzing complex code scenarios
Example:
constscope=createScope("Foo");scope.assert(false,"Nope");// <— expected: throws error `[Foo] Nope` (saves me `[Foo] ` keystrokes (potentially x20 times in some examples)scope.log("hello");// <— expected: logs "[Foo] hello"// etc.
Thus my assert is a method 'by-design' and I want it to be this way.
But I cannot use that due to mentioned TS error:
scope.assert(false,"Foo");// TS: Assertions require every name in the call target to be declared with an explicit type annotation.// Same forconst{ assert }=scopeassert(false,"Foo");// orconstassert=scope;assert(false,"Foo");
The only way to make it work is:
constassert: (input: unknown,message: string)=> asserts input=scope.assertassert(false,"Foo");// TS does not complain and works// ortypeAssert=(input: unknown,message: string)=> asserts input;constassert: Assert=scope.assert;assert(false,"Foo");// I can now re-use `Assert` type, but it is still very manual and misses the point of effective logging / asserting / debugging utility
but doing it this way (explicitly having to assign the type to assert totally misses the point which is my case was - make DX of writing debugging/assert messages easier)
Also, error message is not really friendly. It is hard for me tu guess what exactly TS wants me to do, forcing to Google it and doing a bit deep read (like this issue) to understand it.
It also seems like some internal TS-limitation leaking, rather than some reasonable design decision that makes sense forcing me to not doing what I want to do.
Thus something like :
Typescript currently does not support assert functions that are not explicitly typed at every step. Thus, assert function cannot be part of any object
would be way more friendly and save me some time.
Or even more friendly:
interfaceLogger{assert(input: unknown): asserts input;// <— TS Error: Assert functions cannot be part of interfaces}
this way I would know it earlier and in well-targeted place. This way I know my idea of putting assert inside object is not possible, rather than later thinking I use it in a wrong way.
Just throwing in my two-cents because I got struck by this annoying and very unhelpful bug wording today..
My 'branding' string code.
exporttypeDodiAttributeCodeType=string&{_type: 'DodiAttributeCode'};/* Delcare type to get around TS Bug */typeDodiAttributeCode={guard(str: string): str is DodiAttributeCodeType;parse(str: string): DodiAttributeCodeType;assert(str: string): asserts str is DodiAttributeCodeType;};exportconstDodiAttributeCode: DodiAttributeCode={guard(str: string): str is DodiAttributeCodeType{return!!str;/* some complex test */},assert(str: string): asserts str is DodiAttributeCodeType{if(!DodiAttributeCode.guard(str)){thrownewError();}},parse(str: string): DodiAttributeCodeType{DodiAttributeCode.assert(str);returnstr;},};
import{DodiAttributeCode,DodiAttributeCodeType}from'@core/types/DodiAttributeCode';consta='hello';constb: DodiAttributeCode=a;// TS error 2322, string not validDodiAttributeCode.assert(a);constc: DodiAttributeCode=a;// TS loves it.. Time to do real work now.
constvalue: unknown=0constfoo=(value: unknown): asserts value is number=>{}foo(value)// errorvalue// unknownconstfoo_: typeoffoo=foofoo_(value)// okvalue// number
constvalue: unknown=0constfoo={bar(value: unknown): asserts value is number{}}foo.bar(value)// errorvalue// unknownconstfoo_: typeoffoo=foofoo_.bar(value)// okvalue// number
Also chiming in that I ran into this bug in 4.9.4. I am saddened to hear that the typescript devs believe that this behavior is somehow working as intended.
While I can at least understand the decision to consider this working as intended, the error message you get only makes sense after you discover the solution to the problem, which is not an ideal trait in an error message.
Reacted by Karol Majewski, Chris Krycho, martinml, Dennis Kats, Jaakko Sirén and Alexander “weej” Jones
I think everybody would prefer that asserts and is behave the same in the context of they definition. The current situation:
declarefunctionmakeStruct(): {assert(subj: unknown): asserts subj is stringguard(subj: unknown): subj is string}conststruct=makeStruct()declareconstsubj: unknown// ts-error:// "Assertions require every name in the call target to be// declared with an explicit type annotation. 10:1:416 typescript2775"struct.assert(subj)// No such problem with `guard`if(struct.guard(subj)){subj// `string` as expected}
The current restriction is highly unintuitive and reduces ts community ability to create nice assert method for schema dependent parsers/validators.
For example we could have assert method in zod library structs and typeguard unknown data like this:
Activity
RyanCavanaugh commentedon Feb 21, 2020
Assertion functions always need explicit annotations. You can write this:
justinmchase commentedon Apr 28, 2020
Ryan Cavanaugh (@RyanCavanaugh) Can you explain in a little more detail? I don't understand how to resolve the issue.
This also is failing with the same error:
Using:
How does the exists function not have an explicit annotation? Or what can I do to resolve it in this case.
If I just export the function as non-default and do the import as
import { exists } from "./assert"then it works but I'd like to have them grouped on the objectassert.xyz.mlhaufe commentedon May 6, 2020
Justin Chase (@justinmchase) , @amber-pli
Create a let/const/var and assign it the assert function you imported. Give that variable an explicit type like in the example provided by Ryan Cavanaugh (@RyanCavanaugh) .
justinmchase commentedon May 6, 2020
I guess whats confusing me is that he said:
But I believe in my code the function does have an explicit type annotation, yet its not working. Are we saying there is a bug and that the variable assigned to the function needs the explicit type as well?
borisyordanov commentedon May 6, 2020
Justin Chase (@justinmchase) The type needs to be explicit, it's not a bug
#34523
#34596
justinmchase commentedon May 7, 2020
Ok, one last time, I swear I'm not being intentionally obtuse but I think I'm homing in.
I think my confusion is because, to my understanding, this function has an explicit type:
So when you say the type needs to be explicit its boggling my mind because I don't get how it couldn't be explicit.
But now, I'm wondering if you don't mean the function's type but the type of the argument needs to be explicit? As in the
value: unknownis the issue? It needs to be non-generic and not something such asanyorunknown? Is that what you mean?mlhaufe commentedon May 7, 2020
Justin Chase (@justinmchase) explicitly give it a type where you imported it.
jasonk commentedon May 25, 2020
Justin Chase (@justinmchase) I think the part that is boggling your mind is that the message isn't clear about what exactly doesn't have an explicit type. You quoted this code, which clearly does have an explicit type:
But when you used it, you imported it like this:
The 2775 error you are getting on that line is not about
existsneeding an explicit type, it's aboutassertneeding one. You can verify this by importing exists directly:So the error isn't telling you about problems with exists, it's telling you that your default export needs to be explicitly typed:
It isn't entirely clear to me why that is the case, and it seems that making the error message clearer about where exactly the problem lies was discussed and rejected.
What I have found can work to avoid having to repeat the typings twice is to move the actual assertion functions into a separate file and then re-export them:
I'm also not entirely clear on why this works, but it seems like it does..
justinmchase commentedon May 26, 2020
Ok thanks, I'll try it out!
It does seem like there is a bug in here but I'll settle for work arounds for now.
chriskrycho commentedon May 29, 2020
As a prime example where you would expect this to work, but it doesn't—in a way that seems to me at least to pretty clearly be a bug—is namespaces.
For backwards compatibility, Ember's types have both modern module imports and re-exports from the
Embernamespace. So we have:And we also re-export that on the
Embernamespace, with a re-export like this:I understand the constraints as specified, and I'm afraid I must disagree with the team's conclusion about this error message—
—seeing as it took me about half an hour to finally find my way to this thread and understand the problem, and another half an hour to figure out how to rewrite our DefinitelyTyped tests to continue handling this! 😅
For the record, if anyone else is curious, this is how I'm ending up working around it for the types PR I'll be pushing up shortly:
(I'm also going to have to document that or be ready to explain it, though gladly basically none of our TS users are likely to be doing this.)
kael-shipman commentedon Sep 9, 2020
I tried to include an assertion function as part of the exports for an inner module in one of my libraries and was heartbroken to find that, while I could use it perfectly within the module, I could not use it when referencing it from outside via the module namespace. Ryan Cavanaugh (@RyanCavanaugh)'s answer was a godsend!! Thank you so much!
Here's what I was doing:
Project1::src/Http.ts
Project1::src/index.ts
Project2::src/Functions.ts
I was getting the error in the original post because of this, and I couldn't get past it. I successfully utilized Ryan's solution by doing this:
Project2::src/Functions.ts
While a little awkward, this is a total life-saver and has allowed me to successfully use this wonderful assertion functionality without limitation. (Note that it was necessary to use the
(typeof Http)notation because typescript complained that namespaces can't be used as types.)Anyway, just wanted to add this for anyone who may be struggling with something similar. Thanks again for the tip, Ryan Cavanaugh (@RyanCavanaugh)!
* glob needs adding as a dependency
dschnare commentedon Nov 25, 2020
For what it's worth this has made the Assert library usable again when checking JavaScript with Typescript.
All the type errors reported about non-explicit types are gone (thankfully).
11 remaining items
conartist6 commentedon Oct 11, 2021
Note that if you need method overloading semantics your workaround will need to create an interface:
pie6k commentedon Jan 31, 2022
My use case when it breaks:
scoped 'logging' utility that adds prefix to logs or asserts simply to save me keystrokes and improve dev-experience when debugging or analyzing complex code scenarios
Example:
Thus my assert is a method 'by-design' and I want it to be this way.
But I cannot use that due to mentioned TS error:
The only way to make it work is:
but doing it this way (explicitly having to assign the type to assert totally misses the point which is my case was - make DX of writing debugging/assert messages easier)
Also, error message is not really friendly. It is hard for me tu guess what exactly TS wants me to do, forcing to Google it and doing a bit deep read (like this issue) to understand it.
It also seems like some internal TS-limitation leaking, rather than some reasonable design decision that makes sense forcing me to not doing what I want to do.
Thus something like :
would be way more friendly and save me some time.
Or even more friendly:
this way I would know it earlier and in well-targeted place. This way I know my idea of putting assert inside object is not possible, rather than later thinking I use it in a wrong way.
atulgpt commentedon Feb 6, 2022
I am also facing this issue any update when this bug will be resolved?
WORMSS commentedon Mar 17, 2022
Just throwing in my two-cents because I got struck by this annoying and very unhelpful bug wording today..
My 'branding' string code.
jeremy-rifkin commentedon Apr 6, 2022
Thanks for the clear explanation, this is a strange behavior from typescript. Got stumped by this error today.
nomadshiba commentedon Feb 1, 2023
Really strange behavior
* glob needs adding as a dependency
cmdruid commentedon Jul 12, 2023
Also chiming in that I ran into this bug in 4.9.4. I am saddened to hear that the typescript devs believe that this behavior is somehow working as intended.
nicolo-ribaudo commentedon Aug 16, 2023
Fwiw, if
t.assertSomething(val)reports that error becausetis not explicitly typed, you can solve it by doingso that
t2is explicitly annotated as having its own type.pckilgore commentedon Sep 20, 2023
While I can at least understand the decision to consider this working as intended, the error message you get only makes sense after you discover the solution to the problem, which is not an ideal trait in an error message.
incerta commentedon Feb 16, 2024
I think everybody would prefer that
assertsandisbehave the same in the context of they definition. The current situation:The current restriction is highly unintuitive and reduces
tscommunity ability to create niceassertmethod for schema dependent parsers/validators.For example we could have
assertmethod inzodlibrary structs and typeguardunknowndata like this:WORMSS commentedon Feb 24, 2024
Ryan Cavanaugh (@RyanCavanaugh) is there somewhere to view the 'completed' work?
RyanCavanaugh commentedon Feb 24, 2024
The bulk "Close" action doesn't let you pick which "Close" you're doing.
Design Limitations aren't supposed to be left open - it means we're not aware of any plausible way to address them.