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
Not found a way to provide return type for assert function. Look at use case
Use Cases
functionassert1(e: any,m?: string): asserts e{if(!e)thrownewError();returne;}functionassert2<T>(e: T,m?: string): asserts e{if(!e)thrownewError();returne;// error: T is not assignable to void}declareconstp: Promise<string|undefined>;p.then(assert1).then((x)=>x.length);// error: x is string | undefinedp.then(assert2).then((x)=>x.length);// error: x is string | undefined
This doesn't work also
constassertAndReturn=<T>(x: T)=>{assert(x);returnx;}p.then((x)=>{assert(x);returnx;}).then((x)=>x.length)// okp.then(assertAndReturn).then((x)=>x.length);// error. x is string | undefined
This is by design, according to the PR introducing this features:
A function call is analyzed as an assertion call or never-returning call when
the call occurs as a top-level expression statement, and
the call specifies a single identifier or a dotted sequence of identifiers for the function name, and
each identifier in the function name references an entity with an explicit type, and
the function name resolves to a function type with an asserts return type or an explicit never return type annotation.
The first item states that the call must occur as a top-level statement, this means that the call can't be nested in an expression (such as a parameter to another function or in an assignment). This means that effectively, even if you could return from an assertion function, using it in a position where the return value could be use, the function would not be analyzed as an assertion but rather as a regular function call. So it is best to implicitly just say that an assertion function returns void. This is also outlined in the PR:
An asserts return type predicate implies that the returned value is of type void, and there is no provision for returning values of other types.
Your first example (assert1) allows a return because of a quirk in assignability rules, since e is any it is also assignable to void. Change to unknown and you will get an error.
Suggestion: Anders Hejlsberg (@ahejlsberg) Maybe disallow any return statement that has an expression in assertion functions?
I certainly see the draw of being able to use the same assert function in both ways, though, with some sort of type annotation that encompasses both the asserting and the returning a subset of the values idea.
Reacted by ExE Boss, Pi, Alex Wall and Esteban Echeverry
It's silly IMO. And as i said before - why not to add narrowing at least?
constassertAndReturn=<T>(x: T)=>{assert(x);returnx;}p.then((x)=>{assert(x);returnx;}).then((x)=>x.length)// Works fine p.then(assertAndReturn).then((x)=>x.length);// Doesn't narrow
Reacted by Aideen NasiriShargh, ExE Boss, Pi, Oleg Solomko, matthewvalentine, Stephen Wicklund, Samuel Ainsworth, Zachary Raineri, Sean Alunni, Vlad-Shcherbina and 2 more
I also tried to get around this by having two assert functions, one to assert and one to return, and have the latter call the former. e.g.
function_assert<T=any>(x: T): asserts x{// say, private oneif(!x){thrownewError();}}functionmyAssert<T=any>(x: T): T{// say, public one that returns_assert(x);returnx;}typeEmployee={name: string;}functiongetEmployee(id: number): Employee|null{if(id!==2){returnnull;}return{name: "James"};}constemp=getEmployee(3);console.log(myAssert(emp).name);// Object is possibly null :(// Pure check:// _assert(emp); // without this line, I get "Object possibly null" in the console.log// console.log(emp.name);
But unfortunately, while myAssert is calling _assert(x) on its own, typescript doesn't infer that an assert x is performed. :(
Reacted by Geoff Goodman, Jakub Jirutka, Robbie Speed, Cerino Ligutom III, Leandro Aguiar, Sʜɪᴍᴜʀᴀ Yū, Matthew, Zeno Wu, EnderShadow8, Collin Irwin and 7 more
Ryan Cavanaugh (@RyanCavanaugh) Just checking - was this closed because it's implemented now or because it's not considered or?
Reacted by Jason Papakostas, Mads Felskov Agersten, Zac Murray, Thomas ten Cate, Brian Kim, Collin Irwin, Samuel Ainsworth, Clay Coleman, Artur Baybulatov, Michał Pietraszko and 3 more
@nightlyherb no it doesn't; that function you've written isn't returning an assertion, it's just returning a value. It's not a type guard.
Here is my use case for this
type AssertionType = { toBeUndefined: () => void }
const expect: <T>(bar: T | undefined) => asserts bar is T & AssertionType = (bar) => {
return {
toBeUndefined: () => {},
};
};
// Currently there is an error here
expect(foo).toBeUndefined()
If this feature were implemented in TS, I'd probably submit a contribution to the jest codebase to make certain matchers type guards.
Reacted by Joakim Carlstein and Wessel Kronemeijer
@nightlyherb no it doesn't; that function you've written isn't returning an assertion, it's just returning a value. It's not a type guard.
Here is my use case for this
type AssertionType = { toBeUndefined: () => void }
const expect: <T>(bar: T | undefined) => asserts bar is T & AssertionType = (bar) => {
return {
toBeUndefined: () => {},
};
};
// Currently there is an error here
expect(foo).toBeUndefined()
If this feature were implemented in TS, I'd probably submit a contribution to the jest codebase to make certain matchers type guards.
Having type guard matchers with the Jest matchers interface is a highly wanted feature IMO!
Creating a custom wrapper like expectToBeDefined<T>(value: T | undefined): asserts value is T works but gives bad error reports when the assertion fails
type AssertionType = { toBeUndefined: () => void }
const expect: <T>(bar: T | undefined) => asserts bar is T & AssertionType = (bar) => {
return {
toBeUndefined: () => {},
};
};
// Currently there is an error here
expect(foo).toBeUndefined()
If this feature were implemented in TS, I'd probably submit a contribution to the jest codebase to make certain matchers type guards.
That’s because your code is equivalent to:
typeAssertionType={toBeUndefined: ()=>void}constexpect: <T>(bar: T|undefined)=> asserts bar is (T&AssertionType)=(bar)=>{return{toBeUndefined: ()=>{},};};// Currently there is an error hereexpect(foo).toBeUndefined();
What you want, and is currently impossible:
typeAssertionType={toBeUndefined: ()=>void}// The error is now here:constexpect: <T>(bar: T|undefined)=>(assertsbar is T)&AssertionType=(bar)=>{return{toBeUndefined: ()=>{},};};expect(foo).toBeUndefined();
Activity
dragomirtitian commentedon Oct 22, 2019
This is by design, according to the PR introducing this features:
The first item states that the call must occur as a top-level statement, this means that the call can't be nested in an expression (such as a parameter to another function or in an assignment). This means that effectively, even if you could return from an assertion function, using it in a position where the return value could be use, the function would not be analyzed as an assertion but rather as a regular function call. So it is best to implicitly just say that an assertion function returns
void. This is also outlined in the PR:Your first example (
assert1) allows a return because of a quirk in assignability rules, sinceeisanyit is also assignable tovoid. Change tounknownand you will get an error.Suggestion: Anders Hejlsberg (@ahejlsberg) Maybe disallow any return statement that has an expression in assertion functions?
nmain commentedon Oct 22, 2019
The real world use case outlined here is handled fine by typescript if, instead of
thenchains, you make an async function:sirian commentedon Oct 22, 2019
Nicholai Main (@nmain) There are many other use cases. Especially in arrow functions
nmain commentedon Oct 22, 2019
You can do that easily without getting involved with the assert feature at all:
I certainly see the draw of being able to use the same
assertfunction in both ways, though, with some sort of type annotation that encompasses both the asserting and the returning a subset of the values idea.sirian commentedon Oct 23, 2019
Nicholai Main (@nmain) I know. But this increase entropy - i should have two assert functions with same code;
It's silly IMO. And as i said before - why not to add narrowing at least?
Ryan Cavanaugh (@RyanCavanaugh) Titian Cernicova-Dragomir (@dragomirtitian) Is it hard to add narrowing for code above?
mprobst commentedon Jan 22, 2020
As a data point, both returning a value and narrowing the type of the input is also how Closure's
goog.asserts.assert()& Co work.eyedean commentedon Feb 1, 2020
I also tried to get around this by having two assert functions, one to
assertand one to return, and have the latter call the former. e.g.But unfortunately, while
myAssertis calling_assert(x)on its own, typescript doesn't infer that anassert xis performed. :(LinusU commentedon Feb 7, 2020
I ran into to trying to do this today, my use case is this:
RReverser commentedon Jan 16, 2021
Another datapoint: combining
assertswith an actual return type would allow to improve typings for in-placeObject.assign. See https://twitter.com/RReverser/status/1350524568075710464.RReverser commentedon Jun 2, 2021
Ryan Cavanaugh (@RyanCavanaugh) Just checking - was this closed because it's implemented now or because it's not considered or?
ttencate commentedon Jul 11, 2021
Ingvar Stepanyan (@RReverser) Presumably because #40562 is a duplicate of this one.
RReverser commentedon Jul 11, 2021
Hmm 1) it wasn't linked during closing and 2) it's a newer issue, so I'd expect the newer one would be closed as a duplicate...
scorbiclife commentedon Sep 13, 2022
FWIW the specific
assertAndReturnuse-case seems to work as of TypeScript 4.8.2.https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXygGdCQYMAeAFQD4AKADwC55KBKZoksw+e+LHpXgAyeIQwwsqAOYBuALAAoMHnEFipDAEFUwAEogMyGPgC88KgBo6TFq3inq8AN5L46rhgasFi93CMTXl8AXyUlUEhYBBVUNQAHZgAFGBwAWwEQcnFJGXgAH3g0UEQpEGBqX3iAOgwACxBUWk5NHX1DY1RWWoam7wcnemqIRul6nyUgA
This doesn't cover all use-cases though.
benwainwright commentedon Apr 3, 2023
@nightlyherb no it doesn't; that function you've written isn't returning an assertion, it's just returning a value. It's not a type guard.
Here is my use case for this
If this feature were implemented in TS, I'd probably submit a contribution to the jest codebase to make certain matchers type guards.
joakimbeng commentedon May 31, 2023
Having type guard matchers with the Jest matchers interface is a highly wanted feature IMO!
Creating a custom wrapper like
expectToBeDefined<T>(value: T | undefined): asserts value is Tworks but gives bad error reports when the assertion failsExE-Boss commentedon May 31, 2023
Ben Wainwright (@benwainwright)
That’s because your code is equivalent to:
What you want, and is currently impossible: