ZetCode

TypeScript Expressions

last modified October 15, 2026

An expression is a piece of code that produces a value: 10 + 5, name.length, or age >= 18. Statements, such as if or for, perform actions; expressions compute values. TypeScript uses the same expressions as JavaScript, but it also determines the type of every expression at compile time. This lets the compiler reject operations that make no sense, such as multiplying a string, before the program runs.

Arithmetic Expressions

Arithmetic expressions combine numbers with the +, -, *, /, %, and ** operators.

arithmetic.ts
const sum = 10 + 5;
const difference = 10 - 5;
const product = 10 * 5;
const quotient = 10 / 4;
const remainder = 10 % 4;
const power = 2 ** 10;

console.log(sum, difference, product);
console.log(quotient, remainder, power);
console.log((10 + 5) * 2, 10 + 5 * 2);

All six results have the number type, which TypeScript infers without any annotation. JavaScript has a single number type for integers and floating-point values, so 10 / 4 is 2.5, not 2. The % operator returns the remainder of a division and ** raises a number to a power.

The last line shows operator precedence: multiplication binds tighter than addition, so 10 + 5 * 2 is 20. Parentheses change the order of evaluation.

$ npx tsx arithmetic.ts
15 5 50
2.5 2 1024
30 20

Type Errors in Arithmetic

In JavaScript, arithmetic operators silently convert their operands to numbers. TypeScript allows arithmetic only on numbers, bigint values, and any.

arithmetic_error.ts
const price = "10";
const quantity = 3;

const total = price * quantity;

In plain JavaScript, "10" * 3 evaluates to 30, because the string is converted to a number. Such implicit conversions hide bugs, so TypeScript reports an error. The string must be converted explicitly, for example with Number(price).

$ npx tsc --noEmit
arithmetic_error.ts(4,15): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.

String Concatenation

The + operator also joins strings. If one of the operands is a string, the result is a string.

string_concat.ts
const firstName = "John";
const lastName = "Doe";
const fullName = firstName + " " + lastName;

console.log(fullName);

const items = 5;
const extra = 3;

console.log("Total: " + items + extra);
console.log("Total: " + (items + extra));

Unlike the other arithmetic operators, + with a string and a number is valid in TypeScript: the number is converted to a string. Expressions are evaluated from left to right, so in "Total: " + items + extra the number 5 is first appended to the string, and then 3 is appended to the result. The output is Total: 53. Parentheses make the addition happen first. Template literals, shown later, avoid this pitfall.

$ npx tsx string_concat.ts
John Doe
Total: 53
Total: 8

Comparison Expressions

Comparison expressions compare two values and produce a boolean.

comparison.ts
const a: number = 10;
const b: number = 20;

console.log(a < b, a >= b);
console.log(a === 10, a !== b);

console.log("apple" < "banana");
console.log(0.1 + 0.2 === 0.3);

The strict equality operators === and !== compare values without type conversion and should be preferred to == and !=. Strings are compared character by character, so "apple" < "banana" is true.

The last comparison is false, because floating-point numbers cannot represent 0.1 and 0.2 exactly. TypeScript also reports comparisons that can never be true, for example comparing a number with a string using ===.

$ npx tsx comparison.ts
true false
true true
true
false

Logical Expressions

The logical operators && (and), || (or), and ! (not) combine conditions.

logical.ts
const isAdult = true;
const hasLicense = false;

const canDrive = isAdult && hasLicense;
const canVote = isAdult || hasLicense;
const isMinor = !isAdult;

console.log(canDrive, canVote, isMinor);

The && operator is true only if both operands are true, || if at least one of them is, and ! negates a value. All three variables have the boolean type.

$ npx tsx logical.ts
false true false

Short-Circuit Evaluation

The && and || operators do not always return a boolean. They return one of their operands and evaluate the right operand only if it is needed.

short_circuit.ts
function getNickname(name: string): string {
    console.log(`getNickname called for ${name}`);
    return name.slice(0, 3);
}

const userName: string = "";
const label = userName || "anonymous";
console.log(label);

function isLoggedIn(): boolean {
    return false;
}

const nick = isLoggedIn() && getNickname("Alexander");
console.log(nick);

const count = 0;
console.log(count || 10, count ?? 10);

The || operator returns its first operand if it is truthy, otherwise the second one. The empty string is falsy, so label is "anonymous". The && operator returns its first operand if it is falsy; because isLoggedIn() returns false, getNickname is never called. TypeScript infers the type of nick as false | string, reflecting both possibilities.

The ?? operator is similar to ||, but it falls back only for null and undefined. That is why count || 10 is 10, while count ?? 10 keeps the valid value 0.

$ npx tsx short_circuit.ts
anonymous
false
10 0

Conditional (Ternary) Expressions

The conditional operator condition ? a : b is an expression form of if/else: it evaluates to a if the condition is true and to b otherwise.

ternary.ts
const age = 20;
const category = age >= 18 ? "adult" : "minor";

console.log(category);

const score = 73;
const grade = score >= 90 ? "A" : score >= 75 ? "B" : score >= 60 ? "C" : "F";

console.log(grade);

const limit = Math.random() > 2 ? "none" : 100;
console.log(typeof limit);

Because the ternary operator is an expression, its result can be assigned directly to a const variable. Conditional expressions can be chained, but long chains quickly become hard to read; a switch statement or a lookup table is often clearer.

When the two branches have different types, the type of the whole expression is their union. The limit variable has the type "none" | 100; with let instead of const, it would be widened to string | number. Since Math.random() is never greater than 2, the value is always 100 here.

$ npx tsx ternary.ts
adult
C
number

Template Literals

Template literals are strings delimited by backticks. They can embed any expression inside ${...}.

template_literals.ts
const userName = "Alice";
const items = 3;
const price = 4.5;

const greeting = `Hello, ${userName}!`;
const summary = `${items} items cost ${(items * price).toFixed(2)} EUR`;
const cartStatus = `You have ${items > 0 ? items : "no"} items in your cart`;

console.log(greeting);
console.log(summary);
console.log(cartStatus);

const report = `Name:  ${userName.toUpperCase()}
Items: ${items}`;

console.log(report);

Each ${...} placeholder is evaluated and its value is converted to a string. The placeholders may contain arithmetic, method calls, or even a conditional expression. Unlike strings in quotes, template literals can span several lines; the line break is part of the string.

$ npx tsx template_literals.ts
Hello, Alice!
3 items cost 13.50 EUR
You have 3 items in your cart
Name:  ALICE
Items: 3

Array Expressions

An array literal creates a new array. Array methods such as map, filter, and reduce are expressions that produce new values, so they can be chained.

array.ts
const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(num => num * 2);
const evens = numbers.filter(num => num % 2 === 0);
const total = numbers.reduce((acc, num) => acc + num, 0);

console.log(doubled);
console.log(evens, total);

const sumOfSquares = numbers
    .filter(num => num > 2)
    .map(num => num ** 2)
    .reduce((acc, num) => acc + num, 0);

console.log(sumOfSquares);

const more = [0, ...numbers, 6];
console.log(more);

TypeScript infers numbers as number[]. From that, it infers the type of num in each callback and the result types: doubled and evens are number[] and total is number. None of the methods change the original array.

The chained expression keeps the numbers greater than 2, squares them, and adds the squares: 9 + 16 + 25 = 50. The spread syntax ...numbers inserts the elements of an existing array into a new array literal.

$ npx tsx array.ts
[ 2, 4, 6, 8, 10 ]
[ 2, 4 ] 15
50
[
  0, 1, 2, 3,
  4, 5, 6
]

Object Expressions

An object literal creates an object. TypeScript checks it against the declared type and infers the types of its properties.

object.ts
type Person = {
    name: string;
    age: number;
};

const person: Person = {
    name: "Bob",
    age: 30,
};

const city = "Bratislava";
const key = "country";

const profile = {
    ...person,
    city,
    [key]: "Slovakia",
    isAdult: person.age >= 18,
};

console.log(profile);
console.log(profile.country);

The person object must have exactly the properties of the Person type; a missing property or a misspelled one, such as agee, is a compile-time error.

The profile literal shows other expressions inside objects. The spread syntax copies the properties of person. The shorthand city means city: city. A computed key in square brackets uses the value of an expression as the property name; since key is the constant "country", TypeScript knows that profile has a country property. Property values can be any expressions, such as the comparison in isAdult.

$ npx tsx object.ts
{
  name: 'Bob',
  age: 30,
  city: 'Bratislava',
  country: 'Slovakia',
  isAdult: true
}
Slovakia

Function Expressions

A function can be created inside an expression and assigned to a variable or passed to another function.

function_expression.ts
const add = function (a: number, b: number): number {
    return a + b;
};

const multiply = (a: number, b: number) => a * b;

type Operation = (a: number, b: number) => number;

const subtract: Operation = (a, b) => a - b;

console.log(add(5, 10));
console.log(multiply(4, 6));
console.log(subtract(10, 3));

const result = ((x: number) => x * x)(7);
console.log(result);

The add variable holds a function expression; the multiply variable holds an arrow function, a shorter form whose return type is inferred as number.

In subtract, the parameters have no annotations. TypeScript takes their types from the Operation type of the variable; this is called contextual typing. The last example defines an arrow function and calls it immediately, which is called an immediately invoked function expression (IIFE).

$ npx tsx function_expression.ts
15
24
7
49

The typeof Type Operator

In a type position, the typeof operator gives the type of an expression. This lets us derive a type from a value instead of writing it twice.

typeof_expression.ts
const defaults = {
    theme: "dark",
    fontSize: 14,
    autosave: true,
};

type Settings = typeof defaults;

function applySettings(settings: Settings): string {
    return `${settings.theme}, ${settings.fontSize}px, autosave: ${settings.autosave}`;
}

console.log(applySettings(defaults));
console.log(applySettings({ ...defaults, fontSize: 16 }));
console.log(typeof defaults.fontSize);

The Settings type is { theme: string; fontSize: number; autosave: boolean }, taken from the defaults object. When a property is added to defaults, the type follows automatically.

The last line uses the JavaScript typeof operator, which works at runtime and returns a string such as "number". TypeScript decides which one is meant from the position: in a type annotation or a type declaration, it is the type operator.

$ npx tsx typeof_expression.ts
dark, 14px, autosave: true
dark, 16px, autosave: true
number

Type Assertions

A type assertion, written with as, tells the compiler to treat an expression as a more specific type. It is useful when the developer knows more about a value than the compiler, for example about parsed JSON.

type_assertion.ts
const input: unknown = JSON.parse("[10, 20, 30]");

const values = input as number[];
console.log(values.length, values[0] + values[1]);

const text: unknown = "123";

const wrong = text as number;
console.log(typeof wrong);

const converted = Number(text);
console.log(typeof converted, converted + 1);

JSON.parse cannot know the structure of the data. The assertion as number[] makes the result usable as an array of numbers. The compiler does not check that the assertion is true; if the JSON contained something else, the program would fail later.

An assertion never converts a value. The wrong variable has the type number, but at runtime it is still the string "123", as the output of typeof shows. To get a real number, the value must be converted, here with the Number function. For values of unknown type, narrowing with checks such as typeof is safer than an assertion; see the unknown type tutorial.

$ npx tsx type_assertion.ts
3 30
string
number 124

Best Practices

Source

MDN: Expressions and operators

This tutorial covered TypeScript expressions. TypeScript uses the expressions of JavaScript unchanged and adds a compile-time type to each of them.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all TypeScript tutorials.