Repository navigation
Suggestion: int type #195
Copy link
Copy link
Closed
Labels
Out of ScopeThis idea sits outside of the TypeScript language design constraintsThis idea sits outside of the TypeScript language design constraintsSuggestionAn idea for TypeScriptAn idea for TypeScript
Description
Introducing int type could allows some error to be caught at compile time (like trying to index an array with a float) and perhaps improve performance of outputted javascript, to obtains true int, TypeScript could systematically emit a cas with |0 when a variable/parameter is an integer :
var i: int;
var n: number;
var a: any = 1.2;
i = 1.1; // error;
i = n; // error
i = 3 / 4; // valid, type casted
i = a; // valid, type casted
var indexable: { [i: int]: bool } = {};
indexable[n] = true; // error
indexable[i] = true; // valid
indexable[i/2] = true; // valid, type casted
indexable[a] = true; // valid, type casted
function logInt(i: int) {
console.log(i);
}would emit
var n;
var i = i | 0;
var a = 1.2;
i = 1.1 // error;
i = n // error
i = (3 / 4 ) | 0; // valid, type casted
i = a | 0; // valid, type casted
var indexable= {};
indexable[n] = true; // error
indexable[i] = true; // valid
indexable[(i / 2) | 0] = true; // valid, type casted
indexable[a | 0] = true; // valid, type casted
function logInt(i: int) {
i = i | 0;
console.log(i);
}There will perhaps be a problem with generic and method but I guess the compiler could in this case type cast when passing the parameter:
function add<T>(a: T,b: T): T {
return a + b;
}
var a = add(1, 2); // a is number value 3
var b = add<int>(1/2, 2); // b is int, value 2
var c = add(1/2, 2); // c is number, value 2.5emit :
function add(a, b) {
return a + b;
}
var a = add(1, 2); // a is number value 3
var b = add<int>((1/2)| 0, 2);
var c = add(1/2, 2); // c is number, value 2.5also perhaps the compiler should always infer 'number' if there is not explicit type annotation
var n = 3 //number
var i: int = 3 //intReactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Out of ScopeThis idea sits outside of the TypeScript language design constraintsThis idea sits outside of the TypeScript language design constraintsSuggestionAn idea for TypeScriptAn idea for TypeScript
Activity
RyanCavanaugh commentedon Jul 22, 2014
Great start; thanks for the examples. I have some follow-up questions
Compat of literals vs divide expressions
By what rule is the first line an error, but the second line OK? I have to assume that the type of
1.1is stillnumber, and that the type of3/4is stillnumber, so from a type system perspective there's no difference there.I don't understand either of these. First,
3is not assignable tobool, so the second assignment should definitely fail. Second,indexible[n]being an error implies thatindexible[0]would also be an error (0andnare of the same type), which is very problematic.Treatment of optional
intparametersThe proposed emit of
i = i | 0;implies thatundefinedandnullvalues are implicitly converted to zero. This makes optionalintparameters very dangerous, because you couldn't check them forundefined. How would you deal with that?Emit locations for
| 0Can you clarify the rules for where exactly
| 0would be emitted?fdecampredon commentedon Jul 22, 2014
Ok I guess my example were not really clear, what I meant is :
assigning a
numbervalue to a variable explicitly typed asintshould not be allowed :However the division of 2
int-compatiblevalues is contextually typed tointin place where the compiler expect anintI made an error in this example a correct example would be :
I just wanted to notify that the same rules applied for variable assignment and indexing (and it should also apply to parameters) I'll update the example right now.
Not really in the same way that
var i: int = 0is valid because0is contextually typed tointhereindexible[0]is valid because0is contextually typed toint.nhowever is explicitly typed tonumberand soindexible[n]is an error becausenumberis not compatible withint.there is 4 cases where the compiler must emit
|0var i: intemit :var i = i | 0;int-compatiblevaluesanyintshould be type-casted at the beginning of the function blockIn fact I would tend to think that having for rule that :
intare always initialized and0by defaultlike in other typed language like ActionScript would make sense for me.
If you look at one of the example you can see that
var i: intemit :var i = i | 0;so even for variable int are neverundefinednornull.However that part is completely optional, and the compiler could just type cast parameters passed to the function in the same way that it would do for assignment and indexing :
Still I think that emitting that cast at the beginning of the function could allow JIT compilers to perform some optimization. But that's perhaps another topic.
RyanCavanaugh commentedon Jul 23, 2014
Diving into the contextual typing, it sounds like you have a few new rules:
intindexer is contextually typed with the typeintnumberunless its contextual type isintand it is anIntegerLiteral, in which case its type isintIs that right? Maybe elaborate some examples here.
Going back to this example:
What's the emit here?
i = 3 / 4ori = (3 / 4) | 0? Both are bad -- one introduces a floating point number into an int, and the other breaks JavaScript compatibility (this is not something we would do).fdecampredon commentedon Jul 23, 2014
For me it seems logic than when you assign something to an
intit gets automatically type casted so I would say that :would be translated to :
However that division part of what I proposed is just a tool to avoid boilerplate of manually casting it, if that implicit cast is not something desired the compiler could just report an error.
fdecampredon commentedon Jul 23, 2014
Ok trying to summing up of the discussion I obtain the following rules :
intrepresenting integer valuesintis not compatible is always initialized to0and cannot beundefined, to obtain such behavior the compiler will type castintvalues with|0in place where it might become anon-intvalue :emit:
numberunless its contextual type isintand it is anIntegerLiteral, in which case its type isint:intindexer is contextually typed with the typeintint(+, -, *)intisintint/intisnumber, if we do not include the automatic division type castingnumber( |, &, >>, <<, >>>)numberisint, important since|0it will serve as manual typecast for divisionivogabe commentedon Aug 4, 2014
I have an alternative proposal, which introduces also a double type. This one is more in line with asm.js, for example
|0is not allowed on a number (only integer), you should use~~to convert a number to an integer.Integer & double proposal
This proposal introduces 2 new primitive types:
integeranddouble. Both extends thenumbertype. It was designed for 2 reasons, for compile time type checking and for run time optimalizations (like ASM.js).Naming
I chose for
integerinstead ofint, becauseboolhas been renamed tobooleanin TypeScript, andintegeris more in line withbooleanthanint.ASM.js
This proposal was designed with asm.js in mind.
integermaps to asm'ssignedanddoublemaps todouble.Values
A
doublecan be everything a number can be exceptundefinedornull, includingInfinity,-InfinityandNaN. Anintegercan be every number without a decimal point. Anintegercannot beundefined,null,Infinity,-InfinitynorNaN.numberis the only primitive number type that can containundefinedandnull.When you declare a variable with the type double or integer, it will automaticly be 0.
Any number literal that contains no decimal point and does not have an negative exponent after the
E(like9E-4) is an integer. All other number literals are doubles. The following code will throw errors:Casts
You can cast between the types
number,integeranddouble. The value will be converted at run time (see Generated JavaScript). When converting to an integer, the number will be truncated, the numbers after the decimal point will be removed. So -1.5 will be converted to -1. A value that cannot be converted by truncating (eg undefined or NaN to an integer) will become 0.Optional arguments
A optional argument typed as
integerordoubleis allowed. When no default value is given, 0 is used as the default value (because anintegeror adoublecannot beundefined).Generated JavaScript
Most expressions will be wrapped with
(...)|0(for integers) and+(...)(for doubles).Function arguments
Arguments will be reassigned according to the ASM.js spec.
TypeScript:
JavaScript:
Adding 0 explicitly as the default value should generate the same code as adding no default value.
Function return
The expression of the return statement should be wrapped:
JavaScript:
Assigning to a variable
When declaring a variable (integer or double) without assigning a value, it gets the value 0 (since an integer or double cannot be undefined).
When assigning a something to a variable whose type is
integerordouble, the expression should be wrapped:TypeScript:
JavaScript:
Typecasting
A cast to double is wrapped with
+(...)and a cast to integer is wrapped with~~(...), because the asm.js spec does not allow a double on the | operator.TypeScript
JavaScript
A cast from anyting to number, from integer to integer or from double to double will emit not extra JavaScript.
Operators
Unary
number=>doublenumber=>double(since-undefined = NaNand-null = 0)integer=>integerinteger=>integernumber=>booleanBinary
integer+, - or *integer=>integernumber+, - or *number=>double(sinceundefined + undefined = NaN)number/number=>double(sinceundefined / undefined = NaN)integer|, &, ^, <<, >> or >>>integer=>integernumber<, <=, >, >=, ==, !=, === or !==number=>booleanNote that you cannot apply the
/=assignment operator on an integer.Generated errors
An error is thrown when:
/=on an integer or|=on a number).danquirk commentedon Aug 4, 2014
How does this existing TypeScript code behave? Is it now an error when integer is inferred for x?
It's very strange to add runtime semantics to certain cast operations.
ivogabe commentedon Aug 5, 2014
Indeed, there needs to be a rule that a non-typed variable declaration won't get the type integer or double, but always number. If you want a variable to be an integer or double, you'll need to specify that explicitly.
I chose for runtime semantics to cast operations because of various reasons. For performance, js engines know better how much space they need to allocate for a number and they know which overload of the + operator is used. Integer calculations are most times faster than floating point ones.
There also needs to be a way to convert the different number types between each other. If you already generate JavaScript for cast operations, why not use a cast to convert a number type? Also this doesn't introduce a new syntax.
An alternative would be to write ~~ or + to convert numbers, but in my opinion the cast look better:
mbebenita commentedon Aug 6, 2014
How do you propose to deal with constants in an expression, for instance what does
1 + 2compile to. Is it(1 + 2) | 0or is it left as is. If the constants areintegers, then you'll need to insert the coercion, which will break existing TypeScript code. If you assume that they arenumbersthen the expressionx + 1will always be of typenumber.It feels to me like you need to buy into the asm.js type system explicitly, so either do something like
<integer> 1 + <integer> 2, or come up with a new syntax for declaring typed constants. Otherwise existing TS/JS code will break.ivogabe commentedon Aug 6, 2014
1 + 2isinteger + integer, so it becomesinteger, and it will be wrapped with(...)|0in certain situations.number + integerfalls back tonumber + number(sinceintegerextendsnumber) so this will return adouble, anddoublealso extendsintegerso it will be backwards compatible. Example:Compare it to this OOP example:
When you call
addin this example with a Base and a Bar, it'll return a Foo. The + operator is overloaded the same way in my proposal.number+number=>doublebecause:electricessence commentedon Aug 6, 2014
This is awesome. PLEASE make ints and doubles!
Obviously a simple example of this is array indexes should always take ints.
16 remaining items
dead-claudia commentedon Mar 8, 2018
Tony Arcieri (@tarcieri) I've been cautious on suggesting any of that, since that kind of thing has been repeatedly shot down by the TS team. (They seem to prefer sugar to be syntactic, not type-based.)
styfle commentedon Mar 8, 2018
error (@errorx666) That scenario can already happen with the following code:
status.d.ts
main.ts
main.js
tarcieri commentedon Mar 8, 2018
This may be too much syntax sugar to swallow, but the benefits outweigh the drawbacks, IMO. There is an opportunity here for TypeScript to model sized, typed integers in a way JavaScript VMs can understand, and also statically assert that programs are free of integer/number type confusion.
So, again, for context: we're discussing integer literals.
Every statically typed language I can think of, even where they do support type suffixes/tags, will interpret untagged literals according to the type they're being bound to. So to answer your question: yes, for untagged integer literals, there shouldn't be an error or warning even though the type changed.
If you're worried about type confusion there, yes the tagged syntax should be supported to, and that should fail if a type changes from a BigInt to a number.
tarcieri commentedon Jun 8, 2018
Looks like #15096 is on the TypeScript 3.0 roadmap: https://github.com/Microsoft/TypeScript/wiki/Roadmap#30-july-2018
RyanCavanaugh commentedon Jun 8, 2018
We're still holding the line on type-directed emit. BigInt seems like a "close enough" fit for these use cases and doesn't require us inventing new expression-level syntax.
ivogabe commentedon Jun 8, 2018
Agreed, adding other syntax or types for "double based integers" would only be confusing.
trusktr commentedon Jul 2, 2018
Make it explicit:
and don't do the automatic compile from
3 / 4to(3 / 4) | 0so we don't break expectations of JS. Just let the type system be purely static checks for convenience. It can also be smart. F.e.,3 * 4remains an int,3 * 4.123does not, and perhaps4 * 4.25does produce an int (as subset of number).And same for floats.
trusktr commentedon Jul 2, 2018
Just
int, nothing more. We're in JS, not C. :)tenshikaito commentedon Jan 30, 2019
I want to know when we can use the int declaration symbol