-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Syntax for hinting literal type inference #10195
Comments
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? |
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 |
point is it's the waste of syntax, no one types |
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 |
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 😉 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? 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
|
well yeah, i never said i wanted it implicit, all i want it to get rid of
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 |
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 😅 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 |
And if you'll change a semantic of the |
I'd propose a diamond operator 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:
what it comes down to is the willingness to give a better purpose to some mostly unoccupied syntax |
Just to clarify: 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 |
i am not ready to claim there will be a conflict but my gut filling says that it will complicate parsing at least |
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? 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? @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 |
we don't need implicit typing, we need number literal types, for example:
|
const two = 2; I meant if const targets were implicitly typed by literal types then |
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 ( |
@demurgos I'd argue specific types shouldn't require additional effort as type widening is mostly useful under specific circumstances (mutable variable, i.e. var/let assignment), meaning we already have a decent idea what default makes sense when.
Seems they didn't like this, see #15645. |
While it only works for a limited amount of cases, I would suggest overloading the So, some examples using postfix ! const value = ['a', 1]; // (string | number)[]
const value = ['a', 1]!; // [string, number]
const value = ['a'!, 1!]!; // ['a', 1]
const value = ['a'!, 1!]; // ('a' | 1)[]
const value = {a: 1} // {a: number}
const value = {a: 1!} // {a: 1} cases that it doesn't solve const foo = 'foo' // 'foo'
const bar = [foo!]! // would still be [string]
const value = {a: foo!} // still {a: string} The other syntax solution I can think of (since I like keywords more than characters :P ) is to use const value = {a: foo as const} Or, maybe both? Allow postfix Edit (2019-01-14): @m93a as submitted this as a separate issue as #26979 |
good thing is that we can piggyback ride on the existing TypeScript only expression level syntax bad part is that there is no way to see what is going on in |
My interpretation was that |
Yup, exactly what Ryan is saying. The |
This is great, why can't we focus on just string literal type for now? let value = 'myType'; // string
let value = `myType`; // 'myType'
const value = ['myType']; // string[]
const value = [`myType`]; // 'myType'[]
const value = {a: 'myType'} // {a: string}
const value = {a: `myType`} // {a: 'myType'}
type myType = 'myType'; // OK
type myType = `myType`; // Error
let myType = `myType`; // OK 'myType' |
So what is the current state of this issue? I just checked some examples and noticed that "primitive" expressions seem to be inferred as literal types. Expressions which yield objects, however, stick to the more general types. Also, the compiler distinguishes between constants and variables. Examples: // ✓ type 1
const num1 = 1;
// ✓ type number
const
// ✓ type b => 1 | 2
function test(b: boolean) {
return b ? 1 : 2;
}
// 😕type string[]
const strs = ['a']
// 😕type { key: string }
const obj = {
key: 'value'
} So I guess, the reasoning behind the current inference is: Can this value be changed?
Maybe the problem could be mitigated by telling the compiler more precisely which objects (i.e. standard objects and arrays) are actually constant and which should be changeable. Thus, the Note that I am using Typescript 3.2.2. |
I proposed something like this too in #20195, which is to extend the const o = readonly { x: 3, y: 'hello' };
// o: { readonly x: 3; readonly y: 'hello' } |
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:
The text was updated successfully, but these errors were encountered: