A few things.
First off, the conditional operator is a ternary operator, not a teriary operator.
Second, I note that in your code samples the two code samples which are intended to be equivalent are not:
short foo;
if (isValid)
foo = -1;
else
getFoo();
is not the same as
short foo = isValid ? (short)-1 : getFoo();
The former leaves foo unassigned if isValid is false. The latter assigns foo regardless of the value of isValid.
I assume that you meant
short foo;
if (isValid)
foo = -1;
else
foo = getFoo();
and that furthermore, getFoo() returns short.
The question is why the conversion in the conditional operator without the type cast is illegal but in the consequence of the if statement is legal.
It is legal in the if statement because section 6.1.9 of the specification states:
A constant-expression of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.
-1 is a constant expression of type int that is in the range of short, so it can be converted to short implicitly.
So why is the conditional expression form bogus?
The first thing we have to establish clearly is the rule that the type of the conditional expression is determined from its contents, not from its context. The type of the expression on the right side of an assignment does not depend on what it is being assigned to! Suppose you had
short M(short x){...}
int M(int x){...}
short y = M(-1);
I don't think you'd expect overload resolution to say "well, I'd normally pick M(int) because -1 is an int, but no, I'll pick M(short) instead because otherwise the assignment won't work." Overload resolution doesn't know anything about where the result is going. It's job is to work out what the right overload is based on the arguments given, not based on the context of the call.
Determining the type of the conditional expression works the same way. We don't look at the type its going to, we look at the types that are in the expression.
OK, so we have established that the fact that this is being assigned to short is irrelevant for determining the type of the expression. But that still leaves the question "Why is the type of the conditional expression int rather than short?"
That is a very good question. Let's go to the spec.
The second and third operands, x and y, of the ?: operator control the type of the conditional expression.
If has type X and y has type Y then:
If an implicit conversion exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
If an implicit conversion exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
Otherwise, no expression type can be determined, and a compile-time error occurs.
In this case the operands both have a type. (The verbiage in there about "if x has a type..." is for the case where you have null or a lambda in there; those don't have types!) The first operand is of type int, the second is of type short.
An implicit conversion exists from short to int, but not from int to short. Therefore the type of the conditional expression is int, which cannot be assigned to short.
Now, one could say that this algorithm is not as good as it could be. We could greatly complicate the algorithm to deal with all the cases where there were two possible "candidate" types -- in this case, int and short are both plausible candidates because both branches are convertible to both int and short when considered as specific expressions, rather than simply as having types. We could say in that case that the smaller of the two types was the preferred type.
(Sometimes in C# we say that the more general of two types is the better type, but in this case you would want us to pick the more specific. The language is not consistent in this particular design aspect, unfortunately; I personally would rather we always choose the more specific, but there are type inference scenarios where that would be a breaking change now.)
I considered doing that back in 2006. When designing the behaviour of how LINQ deals with situations where there are multiple types to choose from and one must be picked as "the best" we noticed that the conditional operator already had to solve this problem, and that furthermore, in C# 2 it was not actually implemented according to spec. There was a long debate about this and we ended up making some minor changes to the specification for the conditional operator to bring it more into line with its implemented (and desired) behaviour. However we decided to not take the larger breaking change of tweaking the algorithm to use the smaller of two possible types when there were several to choose from.
For some musings on this problem, see my posts from 2006 on it:
http://blogs.msdn.com/b/ericlippert/archive/2006/05/24/type-inference-woes-part-one.aspx
http://blogs.msdn.com/b/ericlippert/archive/2006/05/26/type-inference-woes-part-two.aspx
http://blogs.msdn.com/b/ericlippert/archive/2006/05/30/type-inference-woes-part-three.aspx