TypeScript Arithmetic Operators: +, -, *, /, %, ** and ++

Every TypeScript arithmetic operator with real results, plus the cases that surprise beginners: decimal division, negative remainders, ** and the compiler errors for strings and unions.

angular-typescript

TypeScript arithmetic operators are the symbols that do math on numbers, namely +, –, *, /, % and **, the increment and decrement operators ++ and —, and their assignment forms such as +=. At runtime, they behave like the JavaScript arithmetic operators. TypeScript adds a type check before the code runs, so the compiler rejects math on a string, a boolean or a string | number value, which is where many JavaScript bugs come from.

We use arithmetic operators for totals, averages, counters and unit conversions. For example, a shopping cart multiplies each price by its quantity and adds the results to get the order total.

The following example uses every operator once, compiled with TypeScript 7.0.2 in strict mode and run on Node.js 22, with the value of each line as a comment.

let a = 10;
let b = 3;

// 1. Basic arithmetic
const sum = a + b;                            // sum = 13
const diff = a - b;                           // diff = 7
const product = a * b;                        // product = 30
const quotient = a / b;                       // quotient = 3.3333333333333335
const remainder = a % b;                      // remainder = 1
const power = a ** b;                         // power = 1000

// 2. Unary operators
const negative = -a;                          // negative = -10
const parsed = +"42";                         // parsed = 42

// 3. Increment and decrement
a++;                                          // a = 11
b--;                                          // b = 2

// 4. Compound assignment
a += 5;                                       // a = 16
a **= 2;                                      // a = 256

Notice that 10 / 3 keeps the fraction, which surprises developers who come from Java.

Next, we look at the cases that confuse new developers, such as division that never truncates and a remainder that keeps the sign. After that, we cover prefix and postfix increment, the compiler errors for strings and union types, floating-point rounding and the bigint type.

1. List of Arithmetic Operators in TypeScript

An operator works on values called operands. Operators come in two kinds, by the number of operands they take.

  • A binary operator, such as *, takes two operands, one on the left and one on the right.
  • A unary operator, such as the minus sign in -x, takes only one.

TypeScript has no separate integer type, so every operand in the table is a number, which is a 64-bit floating-point value.

The examples in the table use x = 10 and y = 4.

OperatorNameExampleResult
+Additionx + y14
–Subtractionx – y6
*Multiplicationx * y40
/Divisionx / y2.5
%Remainderx % y2
**Exponentiationx ** 2100
– (unary)Negation-x-10
+ (unary)Convert to number+”7″7
++Incrementx++x becomes 11
—Decrementx–x becomes 9

The + operator has a second job. When either operand is a string, it joins the two values into a new string instead of adding them, and section 7 shows how TypeScript handles that case.

2. Division Returns a Decimal Result

The / operator always returns the quotient with its fraction. Java and C developers often expect 7 / 2 to give 3, but in TypeScript it gives 3.5. For a whole-number result, we round the quotient ourselves.

// 1. Division keeps the fraction
const half = 7 / 2;                           // half = 3.5

// 2. Integer division
const whole = Math.trunc(7 / 2);              // whole = 3
const truncated = Math.trunc(-7 / 2);         // truncated = -3
const floored = Math.floor(-7 / 2);           // floored = -4

// 3. Division by zero
const inf = 5 / 0;                            // inf = Infinity
const nan = 0 / 0;                            // nan = NaN
const isNan = Number.isNaN(nan);              // isNan = true

The method Math.trunc() removes the fraction, so it rounds toward zero, which matches integer division in Java. The method Math.floor() rounds down, so the two differ for negative numbers, where -3.5 becomes -3 with trunc() and -4 with floor(). For example, a pagination helper that computes the page count from 47 items and 10 items per page needs a whole number, not 4.7.

Dividing by zero does not throw an error. A non-zero number divided by zero gives either Infinity or -Infinity, and 0 / 0 gives NaN (“not a number”). The value NaN is the only value that is not equal to itself, so nan === nan is false, and we check for it with Number.isNaN(). The compiler cannot catch a zero divisor because the value is known only at runtime, so code that divides by user input must check the divisor first.

3. The Remainder Operator (%) and Negative Numbers

The % operator returns what is left after dividing the left operand by the right one. MDN calls it the remainder operator rather than modulo, and the difference shows with negative numbers. The result always takes the sign of the left operand.

// 1. The sign follows the left operand
const r1 = 10 % 3;                            // r1 = 1
const r2 = -10 % 3;                           // r2 = -1
const r3 = 10 % -3;                           // r3 = 1
const r4 = 5.5 % 2;                           // r4 = 1.5

// 2. Common uses
const isEven = 8 % 2 === 0;                   // isEven = true
const day = ((-1 % 7) + 7) % 7;               // day = 6

The remainder also works on decimals, as r4 shows. The last line is the common fix when we need a result that is never negative. For example, a calendar app steps back one day from day 0 in a 7-day week. Adding the divisor and taking the remainder again moves -1 to 6.

4. Exponentiation With ** and **=

The ** operator raises the left operand to the power of the right one. It was added in ES2016 and gives the same result as Math.pow(). A fractional exponent gives a root, and a negative exponent gives the reciprocal. A typical real use is compound interest, where the growth factor is raised to the number of years.

// 1. Powers and roots
const cube = 2 ** 3;                          // cube = 8
const root = 9 ** 0.5;                        // root = 3
const inverse = 2 ** -1;                      // inverse = 0.5

// 2. Groups from right to left
const tower = 2 ** 3 ** 2;                    // tower = 512, same as 2 ** 9

// 3. A negative base needs parentheses
const square = (-2) ** 2;                     // square = 4

// 4. Exponent assignment
let size = 3;
size **= 2;                                   // size = 9

Unlike the other arithmetic operators, ** is right-associative, so 2 ** 3 ** 2 is read as 2 ** (3 ** 2). Writing a negative number on the left without parentheses is a syntax error, because a reader could take -2 ** 2 as either -4 or 4. TypeScript reports the error at compile time.

src/err.ts(1,16): error TS17006: An unary expression with the '-' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.

5. Increment and Decrement: Prefix vs Postfix

The ++ operator adds 1 to a variable and — subtracts 1. Both change the variable itself, so they work only on a let variable or a property, never on a const. Each comes in two forms, and the forms differ in the value the expression returns.

let count = 5;

// 1. Postfix returns the old value
const before = count++;                       // before = 5, count = 6

// 2. Prefix returns the new value
const after = ++count;                        // after = 7, count = 7

// 3. Decrement works the same way
const old = count--;                          // old = 7, count = 6

When the operator stands alone on a line, as in a for loop counter, both forms give the same result. The difference matters only when the expression is also assigned or passed to a function. Applying ++ to a const gives error TS2588, because we cannot assign to a constant. Applying it to a string gives error TS2356, because an arithmetic operand must be a number, a bigint, an enum or any.

6. Compound Assignment Operators

A compound assignment operator does the calculation and stores the result in the same variable, so total += 5 is a shorter way to write total = total + 5. Every arithmetic operator has a compound form. For example, a game adds the points of each round with score += points.

let total = 10;

total += 5;                                   // total = 15
total -= 3;                                   // total = 12
total *= 2;                                   // total = 24
total /= 4;                                   // total = 6
total %= 4;                                   // total = 2
total **= 3;                                  // total = 8

let label = "Total: ";
label += total;                               // label = "Total: 8"

The type of the variable does not change, so adding a number to a string variable with += is allowed, because the result is still a string. The opposite fails. For let price = 10, the line price += “5” gives error TS2322 (type string is not assignable to type number), because the result would be the string “105”.

The logical assignment operators &&=, ||= and ??= follow the same pattern for conditions and default values.

7. Arithmetic on Strings and Union Types

In plain JavaScript, “5” – 2 converts the string without a warning and returns 3, whereas “5” + 2 returns “52”. The different behavior causes real bugs when a number arrives as a string from a form field, a URL or a JSON file. TypeScript checks the operand types and accepts –, *, /, % and ** only for number, bigint, any and numeric enum values.

const qty = "5";
const result = qty - 2;
const flag = 2 + true;

const inputs: (string | number)[] = ["3", 4];
const first = inputs[0];
const doubled = first * 2;

The TypeScript 7.0 compiler reports all three calculations.

src/err.ts(2,16): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
src/err.ts(3,14): error TS2365: Operator '+' cannot be applied to types 'number' and 'boolean'.
src/err.ts(7,17): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

The last error is the one beginners meet most often in TypeScript. The type of first is string | number, a union type that can hold either kind of value, so the compiler cannot allow multiplication. We convert the value, or narrow the type with a typeof check, before doing the math.

// 1. + joins strings
const joined = qty + 2;                       // joined = "52"

// 2. Convert first, then calculate
const n = Number(qty);                        // n = 5
const total = n + 2;                          // total = 7
const unary = +qty;                           // unary = 5

// 3. Bad input
const bad = Number("abc");                    // bad = NaN
const empty = Number("");                     // empty = 0

// 4. Narrow a union with typeof
const value = typeof first === "string" ? Number(first) : first;
const doubled = value * 2;                    // doubled = 6

The compiler accepts qty + 2 because joining strings is valid, so it cannot protect us there. The function Number() and the unary + give the same result. Both return NaN for text that is not a number, and both return 0 for an empty string, so form input needs a check after the conversion. A typeof check narrows union types to one member type.

8. Floating-Point Precision and BigInt

A number stores values in binary, and most decimal fractions have no exact binary form. The classic result is 0.1 + 0.2, which is slightly more than 0.3. The second limit is size, because integers are exact only up to 2 to the power of 53 (Number.MAX_SAFE_INTEGER is 2 to the power of 53 minus 1). A 64-bit database ID, such as a BIGINT primary key, can be larger than that. For larger integers, the bigint type stores every digit.

// 1. Binary fractions are not exact
const sum = 0.1 + 0.2;                        // sum = 0.30000000000000004
const rounded = Number(sum.toFixed(2));       // rounded = 0.3
const close = Math.abs(sum - 0.3) < Number.EPSILON;   // close = true

// 2. Work in whole cents
const cents = 10 + 20;                        // cents = 30
const amount = cents / 100;                   // amount = 0.3

// 3. Integers above 2 ** 53 lose precision
const big = 2 ** 53 + 1;                      // big = 9007199254740992

// 4. BigInt keeps every digit
const exact = 2n ** 53n + 1n;                 // exact = 9007199254740993n
const divided = 7n / 2n;                      // divided = 3n
const mixed = exact + BigInt(5);              // mixed = 9007199254740998n

For money, we store whole cents and convert to a decimal only for display. Rounding with toFixed() is fine for output, but its result must not feed later calculations. A BigInt literal ends with n, and its division drops the fraction, so 7n / 2n is 3n. TypeScript does not allow mixing bigint and number in one expression.

src/err.ts(2,15): error TS2365: Operator '+' cannot be applied to types '10n' and '5'.

We convert one side explicitly with BigInt() or Number(), as the mixed line shows. The bigint type needs target ES2020 or later in tsconfig.json, and the example project uses ES2024.

9. Running the Arithmetic Operator Examples

The example project on GitHub has one source file per section and an index.ts that runs them in order. It uses TypeScript 7.0.2 and needs Node.js 22 or newer. The code that does not compile is shown only in the article.

npm install
npm start

The npm start script compiles the project and prints every value shown in the snippet comments, under one “=== section ===” header per section.

10. Conclusion

TypeScript arithmetic operators are the JavaScript operators with a compile-time type check. Division keeps the fraction, % keeps the sign of the left operand, ** groups from right to left, and the prefix and postfix forms of ++ return different values.

The compiler stops math on strings, booleans and string | number unions, but it still accepts + on a string, so values from user input need an explicit Number() conversion. For results that must be exact, we use either whole cents or bigint. The results of a calculation often go into TypeScript comparison operators next.

11. References

MDN documents the runtime behavior of each operator, and the TypeScript Handbook explains the number and bigint types and type narrowing.

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.