TypeScript: TypeScript Variables and Type Inference

Last updated: 2026-08-26

One of TypeScript’s most powerful features is “type inference”—you don’t need to write type annotations everywhere; the compiler infers them automatically for you. However, you need to understand the inference rules in order to write code that the compiler can interpret correctly.

1. Variable Declaration: let, const, and var

TypeScript fully supports all three ways of declaring variables in JavaScript, but it is recommended to use let and const:

(1) let — a variable that can be reassigned

TYPESCRIPT
let count = 0;
count = 1;        // ✅ let Variables can be reassigned

(2) const — a constant that cannot be reassigned

TYPESCRIPT
const MAX_SIZE = 100;
MAX_SIZE = 200;   // ❌ Error:Cannot be assigned to a constant
TYPESCRIPT
var x = 1;        // ⚠️ Available but not recommended——var There are issues with variable hoisting and scope leakage.
📌 Key Point: Always use let and const; do not use var. If you can use const, use const instead (when the value remains unchanged); use let only when you need to reassign the value. This is fully consistent with JavaScript best practices.

(4) Type Annotations vs. Type Inference

TYPESCRIPT
// Type Annotations——Tell me TypeScript What is the type of the variable?
let age: number = 25;

// Type Inference——TypeScript Automatically determine the type based on the initial value
let name = "Charlie";    // TypeScript Inferred as string
let active = true;    // TypeScript Inferred as boolean

Both approaches produce the same result, but when an initial value is provided, it is recommended to omit the annotation so that type inference can take effect.



2. Complete Rules for Type Inference

Type inference isn't "guessing"—it follows clear rules. Only by understanding these rules can you predict the compiler's behavior.

(1) Rule 1: Inference of Initial Values

When a variable has an initial value, TypeScript infers its type based on the initial value:

TYPESCRIPT
let message = "hello";   // Inferred as string
let count = 42;          // Inferred as number
let flag = true;         // Inferred as boolean
let items = [1, 2, 3];   // Inferred as number[]

(2) Rule 2: Type Inference for const Literals

This is one of the most subtle details of TypeScript type inference—the inference results for let and const are different:

TYPESCRIPT
// let Inferred as a wide type(widening)
let x = "hello";     // Inferred as string
let n = 42;          // Inferred as number
let b = true;        // Inferred as boolean

// const Inferred as an exact literal type(no widening)
const x2 = "hello";  // Inferred as "hello"(Literal types,No string)
const n2 = 42;       // Inferred as 42(Literal types,No number)
const b2 = true;     // Inferred as true(Literal types,No boolean)
💡 Why is it designed this way? let The variable can be reassigned, so it is inferred to have a wider type (a string can hold any string); const The variable remains unchanged, so it is inferred to have the exact literal type. This behavior is called widening.

(3) Rule 3: If no initial value is specified, it is inferred as any

If a variable is declared without an initial value or a type annotation, TypeScript infers it as any:

TYPESCRIPT
let something;        // Inferred as any(Danger!)
something = 42;       // any,Do not check
something = "hello";  // any,Do not check——Completely bypassed the type system
🔥 Common Mistake: any effectively disables type checking, making it no different from writing JavaScript. When strict mode is enabled, variables without initial values will result in an error (you must either explicitly specify the type or provide an initial value).

(4) Rule 4: Best Public Type Inference

When inferring types from multiple sources, TypeScript looks for their "best common type":

TYPESCRIPT
let arr = [1, "hello", true];
// Inferred as (number | string | boolean)[] —— Union Type Arrays

let arr2 = [1, 2, 3];
// Inferred as number[] —— All elements are of the same type,Inferred as an array of element types

▶ Example: Type Inference Experiment

TYPESCRIPT
// Experiment1:let vs const Differences in Inference
let dynamicText = "hello";
const fixedText = "hello";

dynamicText = "world";     // ✅ string Can accept another string
// fixedText = "world";    // ❌ const Cannot be reassigned

// Experiment2:Inference of Object Properties
let user = {
  name: "Charlie",
  age: 20
};
// Inferred as { name: string; age: number }
user.name = "Diana";        // ✅ string Properties can be assigned new values
user.age = 21;             // ✅ number Properties can be assigned new values

// Experiment3:const Object——The property can still be modified!
const config = {
  host: "localhost",
  port: 3000
};
config.host = "127.0.0.1";  // ✅ const Only reassigning the object itself is prohibited.,Non-freezing property
// config = { host: "x", port: 1 };  // ❌ You cannot reassign the entire object
▶ Try it Yourself

3. Basics of Type Compatibility

TypeScript's type compatibility uses "structural subtyping"—types are compatible as long as their structures match, without requiring the type names to be the same.

(1) Subtype Compatibility

TYPESCRIPT
let num: number = 42;
let big: bigint = 100n;

// num = big;    // ❌ bigint No number subtypes of
// big = num;    // ❌ number No bigint subtypes of

(2) Literal types are subtypes of wide types

TYPESCRIPT
let text: string = "hello";    // string Type
const literal: "hello" = "hello";  // Literal types "hello"

text = literal;   // ✅ "hello" is a string subtype of,Can be assigned a value
// literal = text; // ❌ string No "hello" subtypes of
📌 Analogy: The relationship between literal types and wide types is like that between "apple" and "fruit"—an apple is a type of fruit, but fruit isn't necessarily an apple.

(3) "any" refers to the "escape pod"

TYPESCRIPT
let a: any = "hello";
a = 42;          // ✅ any Can accept any type
a = true;        // ✅ any No tests will be performed

let b: string = a;  // ✅ any It can also be assigned to any type.(Danger!)
⚠️ Warning: any violates type safety and should only be used in the following situations: (1) rapid prototyping, (2) migrating legacy JavaScript code, or (3) as a temporary workaround when a third-party library lacks type definitions. any should be avoided in normal development.



4. Combining Type Assertions and Type Inference

If the inference result isn't what you want, you can override it with a type assertion:

TYPESCRIPT
// Scene:DOM Type Inference for Elements
let input = document.getElementById("myInput");
// Inferred as HTMLElement | null——I don't know exactly what element it is.

let inputEl = document.getElementById("myInput") as HTMLInputElement;
// Assert that HTMLInputElement——Tell the compiler"I'm sure this is input Element"
💡 Tip: Type assertions do not perform runtime conversions; they are merely type annotations at compile time. If an assertion is incorrect, problems will still occur at runtime. This will be explained in detail in Lesson 18.



5. Inference Strategies in Actual Development

(1) Strategy 1: If you can infer it, don’t write it down

TYPESCRIPT
// ❌ Redundancy
let name: string = "Charlie";
const MAX: number = 100;
function add(a: number, b: number): number { return a + b; }

// ✅ Concise
let name = "Charlie";
const MAX = 100;
function add(a: number, b: number) { return a + b; }  // The return type is inferred as number

(2) Strategy 2: Interfaces and type aliases must be specified

TYPESCRIPT
interface User {
  name: string;
  age: number;
}

// When an object literal is assigned to a typed variable,Inference+Type checking takes effect simultaneously
let user: User = { name: "Charlie", age: 20 };  // ✅ Structural Matching
let bad: User = { name: "Charlie" };             // ❌ Missing age Properties

(3) Strategy 3: Explicitly Labeling Complex Expressions

TYPESCRIPT
// Return Values of Complex Functions——It is recommended to explicitly label them.,Avoiding Fallacies in Reasoning
function getUserInfo(id: number): { name: string; age: number } | null {
  if (id <= 0) return null;
  return { name: "User" + id, age: 20 };
}

▶ Example: Inference in Practice—Configuration Objects

TYPESCRIPT
// Use as const assertion to make object properties read-only literal types
const THEME = {
  primary: "#4A90D9",
  secondary: "#7B51D9",
  fontSize: 14
} as const;

// Inference Results:
// { readonly primary: "#4A90D9"; readonly secondary: "#7B51D9"; readonly fontSize: 14 }
// Each property is of an exact literal type + readonly

// THEME.primary = "#000";  // ❌ readonly Properties cannot be modified
// THEME.fontSize = 16;     // ❌ readonly Properties cannot be modified

console.log(THEME.primary);   // "#4A90D9"
console.log(THEME.fontSize);  // 14
▶ Try it Yourself

Output:

TEXT 📖 Display only
#4A90D9
14
💡 The purpose of as const: It instructs TypeScript to infer the most precise literal type for the value (without widening) and apply the readonly modifier. It is commonly used in scenarios such as defining constant configurations and action types.


▶ Example: Type Inference with Arrays and Objects

TYPESCRIPT
// Array inference — element type is inferred automatically
let scores = [90, 85, 92];           // number[]
let names = ["Alice", "Bob"];        // string[]
let mixed = [1, "two", true];       // (number | string | boolean)[]

// Object inference — each property gets its own type
let point = { x: 10, y: 20 };       // { x: number; y: number }
let user = { name: "Eve", active: true }; // { name: string; active: boolean }

// Nested inference
let config = {
  server: { host: "localhost", port: 3000 },
  debug: true
};
// { server: { host: string; port: number }; debug: boolean }

console.log(config.server.host);    // "localhost"
console.log(typeof mixed[0]);       // "number"
▶ Try it Yourself

Output:

TEXT 📖 Display only
localhost
number

❓ FAQ

Q When is it necessary to manually specify type annotations?
A In three situations: (1) Function parameters—when there is no initial value for type inference; (2) Variable declarations without initial values—otherwise, the type is inferred as any; (3) Return types that do not match expectations—when the inferred type is too broad or too narrow, it must be overridden.
Q What practical implications does the difference in type inference between let and const have?
A let is inferred as a broad type (such as string or number), while const is inferred as a literal type (such as "hello" or 42). This difference primarily affects the matching of union types and literal types. For example, when defining a string constant with const, the type is inferred as an exact literal, which can be used as a discriminator for union types.
Q What is the relationship between any and TypeScript? Is it a good idea to use any?
A any is TypeScript’s “type escape hatch”—it disables type checking, effectively reverting to JavaScript mode. You should not use any unless you’re migrating legacy code or temporarily working around a type issue. If you’re unsure of a type, using unknown (explained in detail in Lesson 5) is safer than using any.
Q What is as const? How does it differ from regular const?
A Regular const only prevents a variable from being reassigned; type inference still allows widening (e.g., const x = "hi" is inferred as "hi", but object properties remain of the wider type). as const is a type assertion that causes all levels to be inferred as literals plus readonly. Only by using as const with objects or arrays can you achieve true deep read-only behavior.

📖 Summary

📝 Exercises

  1. Basic Exercise (Difficulty ⭐): Declare a string variable using let and another using const, and print their values. Then try to reassign them and observe how TypeScript handles the error.
  2. Advanced Problem (Difficulty ⭐⭐): Define a configuration object as const named COLORS that contains three hexadecimal color values: red, green, and blue. Then try modifying one of the colors and observe the error message generated by the readonly constraint.
  3. Challenge (Difficulty: ⭐⭐⭐): Write a function createUser(name: string, age: number) without specifying a return type, allowing TypeScript to infer the return type. Then, use a typed variable outside the function to receive the return value and verify whether the inference is correct.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏