Closed zpdDG4gta8XKpMCd closed 5 years ago
Can you explain what the problem with this syntax is? Too verbose? This works in Typescript today.
interface Tuple {
[0]: string;
[1]: number;
}
var value: Tuple = ['a', 1]; // type checks correctly
In fact, just tested and this works too:
interface Tuple<U, T> {
[0]: U;
[1]: T;
}
var value: Tuple<string, number> = ['a', 1];
I assume the problem here is that you need to manually annotate the value? You want sane implicit typing of literals?
Problem: There is no way to get a literal type value without explicit type annotation.
Ah, gotcha. Yeah, I hardly ever use implicit types, I manually type everything explicitly. I get the need for this proposal, but I personally think the proposed syntax is unexpected behavior. I don't think it will break anything, but there seem to be a lot of edge cases and as a user, I don't expect (true) to type differently than true. I don't like surprises in my type checking.
Personally, I would just write this and make the intent explicit:
const value = ['a', 1] as [string, number];
Of note, you can't use literal types in "as" notation, which I would propose to fix this issue personally:
const value = true as true;
could you give me an example when you casually type (true)
, please?
point is it's the waste of syntax, no one types (true)
in their everyday work, why not to give it a better use?
A quick search of my code base of the previous big project I did in Typescript gives me this:
var borderWidth = (4);
...
borderWidth = 12;
I assume this breaks with your proposal? In which case, yes, your proposal would break the last big codebase I worked on. Why are those parenthesis there? I assume at one point it said something like borderWidth = (4 * someOtherValue);
. Real life code bases are messy, someone forgot to take those parenthesis out. I wouldn't mind something like that breaking with an update of TypeScript btw, just saying that people do casually type stuff like that.
you are lucky to find one place that, well, was left unattended, rather than crafted the way it is on purpose, and it's just one scalepan... - your unintentionally overlooked code, the other scalepan is a new feature that enables the whole new world of exciting opportunities and universal happiness, now what exactly are we arguing about?
We're not arguing, I said from the beginning I like the idea of the feature :wink:
I'm just not sure if the proposed syntax is to my liking, seems a bit unexpected. Then again, as I said, I don't use implicit typing, so it really doesn't mean much to me.
then give me some thumb's up's!
unexpectedness of the syntax is already spotted (in the parent proposal), admitted and listed here under "Shortcomings"
i confess i lived a sinful life, the proposal is not 100% perfect
Just one more observation: if I have to manually type those parenthesis, then I'm explicitly annotating that literal. I don't think your proposal is implicit annotation at all, it's just a shorthand explicit annotation. The shorthand is universal and saves you from explicitly mentioning the type, but it's explicit regardless. Anyway, I'm knee deep in physics integrators right now, time to get back.
hmmm how about a new operator? :=
This way, if the compiler, by any chance can infer the value it will explicitly assing the type.
const a := 1; // a is 1
// Can work with expression
const b := (Math.random() * 0); // b is 0
// As it won't be confused with explicit parentheses like:
const b = (Math.random() * 0) + 1; // b is number even though b is always 1
const c := Math.random(); // c is number
const d := 'hello'; // d is 'hello'
const e := !false; // e is true
:=
kinda aliases explicit type annotation const a: 1 = 1
=> const a := 1
, the syntax is new and kinda expected :grin:
well yeah, i never said i wanted it implicit, all i want it to get rid of
... explicit type annotations
while still being explicit about my intentions at defining a literal value
Yup, I get it now. I looked over your initial thread too. I'll give this a thumbs up, I do think it would be useful and it does have a parallel to the arrow syntax.
@alitaheri good catch!
i agree the condition expression of the ternary operator is usually tend to be braced in parenthesis
here is the thing though:
a literal expression is an expression whose terms DO NOT contain variables
i hate the flatness of this statement but nevertheless it would enable what's required it if accepted
@alitaheri
:=
is limiting it to assignment cases only
how about binding arguments to parameters?
function id<a>(value: a): a { return value; }
const value = id('hey');
@aleksey-bykov Yeah good point, I guess inferring value isn't that simple anyway :sweat_smile: and assignment-only operator will limit the usage a lot! specially with tuples O.o
I'm supporting the idea that we need a syntax for the literals (as well as for literal tuples, literal records). However, I'm seriously in doubt about (
)
. Mostly because of code generation tools. The analogy between {}
and ({})
in the body of the arrow function is incorrect: in the case of arrow functions we have simply parsing disambiguation hint between Expression and BlockStatement.
What you are proposing is completely different. We know that (
are )
are used only for grouping at the expression level.
And if you'll change a semantic of the (
)
- you have chances to seriously interfere with existing code generation tools and approaches.
I'd propose a diamond operator <>
as the most concrete type.
var a = <>1;
var a = 1 as <>;
or even an empty type annotation.
var a = <>1;
var a = 1 as ();
However, both variants are ugly :(
@Artazor, it's a good point too that you mentioned...
a couple of thoughts:
(a + b) * c
are what the grouping expression is intended for, we are not trying to mess with them, as i said it only matters for literal expressions, so we are safe here(1 + 2)
are, well, although possible, but, pardon my arrogance, quite rare AND! we can be still statically correct here, meaning we can infer 3
from that expression if it matterswhat it comes down to is the willingness to give a better purpose to some mostly unoccupied syntax
Just to clarify: (1 + 2)
and (1)
are rare in the hand-written code, but is pretty often observable in a generated code (macro-expansions and so on), that was my concern.
Are the following three code snippets equivalent?
var a = (1);
const ONE = 1 as 1;
var a = (ONE);
import {ONE} from "./constants"; // assume export const ONE = 1 as 1
var a = (ONE);
@aleksey-bykov I appreciate the need for this and the generality of this proposal as compared to #9217, but I share the concerns expressed by @Artazor. I think the use of parens to indicate these types is very problematic. I suggest simply replacing them with <
and >
which would then be removed from the emitted JavaScript. At that point I would be all for this proposal.
<
and >
already have very distinct purpose:
<true>false</true>
i am not ready to claim there will be a conflict but my gut filling says that it will complicate parsing at least
Are the following three code snippets equivalent?
Yes.
var a = (1); // "a" is 1: new syntax at play
Yes.
const ONE = 1 as 1; // ONE is already a literal number
var a = (ONE); // "a" is 1: since we have a constant (not a literal) the parenthesis are ignored here, but the type of the expression is still literal number as stated above
Yes.
import {ONE} from "./constants"; // assume export const ONE = 1 as 1
var a = (ONE); // "a" is 1: very much like the previous case, importing doesn't change anything
Have the consequences of having implicit literal types on const targets been investigated? I expect no problems with that. If it were introduced that it could be like this:
var one = 1; // number
const two = 2; // literal type 2
var three = two + 1; // ok, number
var otherTwo = two; // now, it's literal type 2, which is undesirable. Maybe downcast it to number here?
On other hand, do we really need it? Why would you need to implicitly type literal type? From my experience, that is common to have literal types references on interfaces in order to discriminate them. So that means, you would already have explicitly annotated literal type.
@aleksey-bykov do you have any real world examples in mind?
not sure what you are talking about, there wasn't anything said about implicitness (everything is strictly explicit)
in your example
const two = 2;
the constant two
is of type number
, not 2
we don't need implicit typing, we need number literal types, for example:
type Direction = number
we say type Direction = -1 | 1;
the benefit of going with the latter is eliminating a chance to get 0
which doesn't have any meaning where only -1
or 1
are expectdconst two = 2;
I meant if const targets were implicitly typed by literal types then two
would be of type 2
.
again, there was absolutely nothing said about implicit typing, we are talking about a special syntax to explicitly specify that the type of a numeric literal is a literal type (1
or 2
or ...) rather than a number
consider:
const two = 2; // two is number (traditional TS)
const two : 2 = 2; // two is 2 (a new literal numeric type since TS 2.0.1 which hasn't been yet released)
const two = <2> 2; // another way of saying two is 2 (also valid in TS 2.0.1)
const two = (2); // the PROPOSED way of saying the same, hence the discussion
If you are not annotating type explicitly then it will be inferred implicitly :)
const one: 1 = 1; // explicit
const two: (2); // or whatever syntax you want, implicit
i think that you might need to read the following to refresh your memory about literal types: https://github.com/Microsoft/TypeScript/pull/9407
@Igorbek, he is correct, if the only reason a user should use parens is to indicate literal type, then it is explicit typing (think of it as a shorthand for "1 as 1").
@aleksey-bykov, he has a point, that if we make it so the compiler will always choose the most specific type for const values, there is no need for your shorthand at all. We could just tell the compiler to always choose literal types when dealing with a const (the most specific type) and your issue goes away entirely (unless you also want to use this new syntax for regular vars, but I don't think you do).
@SimonMeskens thank you for mediation :)
So there are 2 sorts of types: the red ones and the blue ones. Currently the red ones are dominating by being the only ones to have a literal representation for their values. The blue ones are oppressed by not having a way to get their values declared without explicit annotations. This is a horrible example of discrimination we say. Let's turn it the other way around by letting the blue ones have their values right off the bat while requiring the annotation for the red ones, so that they know what it feels like to be humiliated, right? Can we celebrate yet? Looks like we just solved the problem, didn't we?
Now when we say: var x = 1; we can be sure that we protected ourselves from all sort of nonsense like x = 2; or x = x + 1;
Case closed, class dismissed
On Aug 7, 2016 7:27 PM, "Simon Meskens" notifications@github.com wrote:
@Igorbek https://github.com/Igorbek, he is correct, if the only reason a user should use parens is to indicate literal type, then it is explicit typing (think of it as a shorthand for "1 as 1").
@aleksey-bykov https://github.com/aleksey-bykov, he has a point, that if we make it so the compiler will always choose the most specific type for const values, there is no need for your shorthand at all. We could just tell the compiler to always choose literal types when dealing with a const (the most specific type) and your issue goes away entirely (unless you also want to use this new syntax for regular vars, but I don't think you do).
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/Microsoft/TypeScript/issues/10195#issuecomment-238115680, or mute the thread https://github.com/notifications/unsubscribe-auth/AA5PzQ9ADqDtD8X4g7Y_erSBaXXiq_7fks5qdmn5gaJpZM4Jei5a .
My point is that depending on the situation we need either one sort of types or another. Effectively we need them both equally bad. The current way of doing ones is too clumsy. And any solution that favors one over another is equal awkward. The problem with picking the most limiting type is 1. a massive breaking change, 2. not always the desired behavior, 3. doesn't fix anything just articulates the same problem in a different way
If there is a solution, it should make the use of both the traditional and literal types equally comfortable, ideally without breaking much stuff along the way.
I can't see a single use case where a more specific const breaks anything. You can't change it, you can only use it and you can use a more specific type any place you can use the more general type. Const values should always be as specific as possible. Could you give an example of code that breaks by making a const value a more specific type?
Couple of cases that aren't immediately clear with picking most limiting type:
const zero = [0, 0]; // what do we get here?
var direction = Math.random () > 0.5 ? -1 : +1; // how can i flip it?
zero
is [0, 0]
, direction
is number
(I'd prefer that for vars) or -1 | 1
(stricter)
const at = zero;
at[0] += 1; // shall we break?
function flip(direction: -1 | 1): -1 | 1 {}
var direction = Math.random () > 0.5 ? -1 : 1;
direction = flip (direction); // shall we break?
function toBacked<a> (defaultValue: a) {
return function backed (value: a) {
return value || defaultValue;
}
}
const backed = toBacked (1);
const x = backed (0); // shall we break?
Since they are called unit types, what about this:
// These variables will have the literal type of the given value
var pi = <unit> 3.14;
var option = 'strictNullChecks' as unit;
const TRUE = <unit> true
Casting to unit
means taking the unit type of the literal value being cast.
Reasoning:
(true)
idea, arguably):=
idea)unit
keyword only appears in type positions so can't clash with any JavaScript meaning, either present or futureI don't get your examples aleksey. None of them use literals:
const zero = [0, 0]; // not a literal, irrelevant
var direction = Math.random () > 0.5 ? -1 : +1; // var is not const, example is irrelevant
const backed = toBacked (1); // not a literal, irrelevant
< and > already have very distinct purpose:
type assertions: <1>1
html tags:
@aleksey-bykov just to be clear, I was proposing
const one = <1>;
as a syntactic sugar for the forms
const one = 1 as 1;
and
const one = <1>1;
and
const one: 1 = 1; // different but has the same effect in this context
I do not believe this conflicts with anything but you are correct that it would complicate parsing with JSX enabled.
@SimonMeskens
If I understood the idea about using the most limiting type for numbers, the following rules will govern it:
number
var
or let
declarations its type widens to number
number
By following these 3 rules we are looking to achieve the following goals:
The following are the examples demonstrate the shortcomings of it:
Breaking change (used to work as written before R1)
const at = { x: 0, y: 0 }; // according to R1 "at" has the type "{ x: 0, y: 0 }"
// the result of the following expression is a "number" according to R3
at.x +=1; // compiler error: since "x" is always "0" it cannot be assigned a "number"
The annotations are still required in many cases like the following one, so G1 is not achieved
function flip(direction: -1 | 1): -1 | 1 { return direction > 0 ? -1 : 1; }
// here we need to mutate a variable of type "-1 | 1" which we wish
// could be inferred from the expression thank to R1 but cannot be due to R2,
var direction = Math.random () > 0.5 ? -1 : 1; // direction is number
// so a call to the "flip" function would give a compilation error:
direction = flip(direction); // cannot use a number where "-1|1" is expected
// in order to make this example work we need explicitly annotate either
// the "direction" or the number literals, which undermines the whole idea
Breaking change (used to work as written before R1)
function toBacked<a>(defaultValue: a) {
return function backed(value: a) { return value || defaultValue; };
}
const backed = toBacked(1); // thank to R1 the function "backed" is of type "(value: 1) => 1"
const x = backed(0); // compiler error, "0" cannot be used where "1" is expected
@yortus, i like your idea a lot, it might be worth creating a separate request for it
@aluanhaddad, the following will suffer from the same problems as HTML syntax markup in JSX templates conflicting with type assertions: https://github.com/Microsoft/TypeScript/issues/296
const one = <1>;
For the record, parentheses used to "stop" contextual typing, but that was a horrible idea because it tripped a lot of people up. It was extremely subtle so I think it's out of the question.
Let the syntactic bikeshedding continue. :bike: :house:.
@DanielRosenwasser what about this one: https://github.com/Microsoft/TypeScript/issues/10195#issuecomment-238133905 ?
It is not semantically correct to use the same type. These types are singletons, but they are distinct. The unit type is actually a void type.
Now that we have so many literal types we more than ever need new syntax that would make their use natural. Please consider the following:
Problem:
Solution:
Highlights:
Shortcomings:
Prior work: