Closed
Description
Sometimes it's necessary (e.g. for guiding type inference, for ensuring sub-expression conforms to an interface, or for clarity) to change the static type of an expression. Currently TypeScript has the as
(aka <>
) operator for that, but it's dangerous, as it also allows down-casting. It would be nice if there was another operator for implicit conversions only (type compatibility). I think this operator should be recommended in most cases instead of as
.
This operator can be implemented as a generic function, but as it shouldn't have any run-time effect, it would be better if it was incorporated into the language.
function asType<T>(value: T) {
return value;
};
EDIT: Due to parameter bivariance, this function is not equivalent to the proposed operator, because asType allows downcasts too.
Activity
RyanCavanaugh commentedon Mar 11, 2016
Can you post a few examples of how you'd like this to work so we can understand the use cases?
magnushiie commentedon Mar 12, 2016
One example very close to the real-world need (I'm trying to get react and react-redux typings to correctly represent the required/provided Props):
I've called the proposed operator in the last snippet
is
.The other kind of scenario is complex expressions where it's not immediately obvious what the type of the expression is and helps the reader understand the code, and the writer to get better error messages by validating the subexpression types individually. This is especially useful in cases of functional arrow function expressions.
A somewhat contrived example (using the tentative
is
operator again), where it's not immediately obvious what the result of getWork is, especially when it's a generic function where the result type depends on the argument type:DanielRosenwasser commentedon Mar 12, 2016
I ran into something similar when I was patching up code in DefinitelyTyped - to get around checking for excess object literal assignment, you have to assert its type, but that can be a little extreme in some circumstances, and hides potential issues you might run into during a refactoring.
There are also scenarios where I want to "bless" an expression with a contextual type, but I don't want a full blown type assertion for the reasons listed above. For instance, if a library defines a type alias for its callback type, I want to contextually type my callback, but I _don't_ want to use a type assertion.
In other words, a type assertion is for saying "I know what I'm going to do, leave me a alone." This is more for "I'm pretty sure this should be okay, but please back me up on this TypeScript".
RyanCavanaugh commentedon Mar 14, 2016
Sounds a lot like #2876?
magnushiie commentedon Mar 14, 2016
If I understand #2876 correctly, it's still a downcast (i.e. bypassing type safety). What I was proposing here is an upcast (i.e. guaranteed to succeed at runtime or results in compile time error). Also, while
<?>
seems a bit like magic, theis
operator is as straightforward as assigning to a variable with a defined type or passing an argument to a function with a parameter that has a defined type.I think the best example of this operator exists in the Coq language:
Here, the expression
x : nat
is a type cast, where Coq's type cast is not dynamic but static (and mostly used in generic scenarios, like the ones I mentioned above) - here it means id_nat's argument type is restricted to be a nat.chilversc commentedon Jun 9, 2016
Another case for this is when returning an object literal from a function that has a type union for it's return type such as
Promise.then
.This gets quite tricky for intellisense in VS because the return type from
then
isPromiseLike<T> | T
. Casting allows intellisense to work, but as mentioned it can hide errors due to missing members.Also the error messages when the return value is invalid are quite obtuse because they refer to the union type. Knowing the intended type would allow the compiler to produce a more specific error.
magnushiie commentedon Sep 14, 2016
@chilversc I'm not sure how an upcast can help with your example. Could you show how it would be used, using the above asType function (which is the equivalent to the operator I'm proposing). Note that due to parameter bivariance, the current compiler would not always give an error on invalid cast.
chilversc commentedon Sep 18, 2016
Odd, I thought I had a case where an assignment such as
let x: Foo = {...};
would show a compile error while a cast such aslet x = <Foo> {...};
would not.The cast was required to get the object literal to behave correctly as in this case:
normalser commentedon Sep 28, 2016
Could we just use
is
the same way asas
?aluanhaddad commentedon Dec 29, 2016
@wallverb that is really clever and really intuitive. Interestingly, it also provides a manifest way of describing the difference between the assignability between fresh object literals target typed by an argument vs existing objects that conform to the type of that argument.
164 remaining items