all symbols within a 'with' block will be typed as 'any'

Topics: Language Specification
Nov 17, 2012 at 6:45 AM
Edited Nov 17, 2012 at 7:06 AM

The with statement is only partially supported:

 

module a{
	export function b():void{
		alert("yo");
	}
}

with (a){
	b();	
}

 

It works, but as the compiler says "all symbols within a 'with' block will be typed as 'any'".

Can we expect this to change? It would be really nice to be able to have the types also within 'with's.

(Edit: I just realized that 'with' is not allowed in Ecmascript 5 strict mode.)

Coordinator
Nov 20, 2012 at 4:15 PM

This is unlikely to change. 

As you pointed out, it's fallen out of favor (it's in the Bad Parts in Crockford's book http://oreilly.com/javascript/excerpts/javascript-good-parts/bad-parts.html). 

Nov 20, 2012 at 5:21 PM
Edited Nov 20, 2012 at 5:22 PM

So you are saying that we can't use one of the features in Javascript when using TypeScript?

That is pretty much crap because I have variables that need to be casted and the structure of the modules will make it pretty long. And I have to set multiple properties inside those variables. Which and without the with statement it forces me to repeat it 10 times in a row.

Coordinator
Nov 21, 2012 at 4:04 PM

One way to think of TypeScript is a linting tool for JavaScript (plus a few features).  You can always ignore the suggestions TypeScript has - in a sense type errors are really only type warnings.  For example, you might want to do:

var x = {} + {}

It's perfectly valid JavaScript.  TypeScript will give you a warning, but it will still output the JavaScript you requested.

Conversely, TypeScript doesn't give you typing help in a few cases and instead goes to 'any'.  This helps to simplify typechecking so that the compiler can use type inference more often, and to help that inference be pretty efficient.  It's a trade-off. 

 

Sep 27, 2013 at 10:57 AM
Sorry to bring up this old thread, but couldn't find a more recent one that deals with the with statement.

Shouldn't the compiler just give a warning instead of an error?
(That brings up the question if there is the concept of warnings in the context of the TS compiler?)

Currently, the compiler outputs "error TS2135: All symbols within a with block will be resolved to 'any'." and sets the errorlevel to 1 (on Windows).

Also, as with eval, I guess there are situations where it is OK to use with. Just because it is on Crockford's 'bad parts' list doesn't mean it is a complete no-go. After all, he discourages the use of the ++ operator, blockless statements (e.g. IF without braces), and to some parts even the new operator :).

I am not arguing in favor of full support for with, just allow it to be used without causing the compile to 'fail' (in the broader sense).
Developer
Sep 27, 2013 at 8:08 PM
The 'error' at present is basically a warning, similar to all of our type checking related errors. That is, the compiler gives you the error but still emits JS to the best of its ability. At some point it will make sense to do a proper error/warning division and 'warn as error' and all that good stuff.
Oct 4, 2013 at 9:50 AM
Since TypeScript is statically typed, it can safely resolve symbols inside with block and rewrite code to not depend on JavaScripts's with thus avoiding its inherent issues. I have requested this in #1753.
Developer
Oct 4, 2013 at 7:20 PM
The nature of a 'with' statement makes this largely impossible. From your example:
with (foo) {
   bar();
}
to
foo.bar();
These are not equivalent. If foo does not have a bar() property then bar() could be a global function. If nested within other with statements or scopes it could be even hairier. The same logic here http://www.yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/ for why the 'with' statement can introduce ambiguity in JavaScript makes the TypeScript compiler unable to safely type code within a 'with' statement by anything other than 'any'. As noted in that blog post, it is simpler and safer to just use a var to alias a long dotted name (or sometimes an import alias in TypeScript).
Oct 4, 2013 at 7:57 PM
If foo does not have bar property then bar either resolves statically to symbol in outer scope or compilation fails - just as if there was no with block around it.

Ambiguity exist only in absence of static type information. But since typescript compiler knows type of foo and knows the set of possible members of foo, it can safely rewrite bar() into foo.bar() if it exists in foo.
Obviously generated code would depend on the type of foo and expressions resulting in any have to be prohibited.
Coordinator
Oct 7, 2013 at 5:35 PM
Edited Oct 7, 2013 at 5:44 PM
The compiler doesn't know the complete set of members of foo, only a minimum subset. For example:
interface Things {
    foo: number;
    bar: number;
}

var x: Things;
if(Math.random() > 0.5) {
    x = { foo: 3, bar: 5, baz: 10 };
} else {
    x = { foo: 3, bar: 5 };
}

var baz = 'what';
with(x) {
    console.log(baz); // ???
}
Oct 7, 2013 at 5:57 PM
Edited Oct 7, 2013 at 6:00 PM
I don't know why you would assume that baz inside with(x) should be somehow resolved dynamically.

Since x is Things and Things does not have baz then baz inside with refers to var baz from outer scope.

But I agree that it's not really that important and I'd rather see progress on other language features.
Oct 7, 2013 at 6:05 PM
By "outer scope" I mean whatever's outside with() block.
Coordinator
Oct 7, 2013 at 8:54 PM
A core language principle is that we don't change the semantics of existing JavaScript code.
Oct 7, 2013 at 9:13 PM
Ha! But the semantics would be preserved.

Only some programs that are valid javascript would fail to compile but that is no different from the way typescript works already (e.g. adding console.log(x.baz) in toplevel above would fail to compile even though it's a valid js).
Oct 7, 2013 at 9:14 PM
Edited Oct 7, 2013 at 9:31 PM
mwisnicki wrote:
Ha! But the semantics would be preserved.
Ah sorry, I'm mistaken. When x.baz is defined js would print "10" while ts would print "what".