# Variables and Types

> Source: https://learn-typescript.org/variables-and-types/
> Part of Learn TypeScript, free to read.

TypeScript is JavaScript with a type checker on top. Every valid JavaScript file is valid TypeScript — the difference is that TypeScript reads your code before it runs and tells you when something cannot possibly work.

## Declaring a variable

```typescript
let count = 0;          // can be reassigned
const name = "Ada";     // cannot be reassigned
```

Use `const` by default and `let` when you genuinely need to reassign. **Never use `var`** — it is function-scoped rather than block-scoped, which produces surprising behaviour, and there is no situation in modern code where it is the right choice.

```typescript
if (true) {
  var leaks = "visible outside this block";
  let contained = "not visible outside this block";
}
console.log(leaks);      // works — this is the problem
console.log(contained);  // error: Cannot find name 'contained'
```

## Type annotations

You can tell TypeScript what a variable holds with `: type` after the name.

```typescript
let age: number = 36;
let username: string = "ada";
let isActive: boolean = true;
```

Now the checker holds you to it:

```typescript
age = "thirty-six";
// Type 'string' is not assignable to type 'number'.
```

That error appears in your editor as you type — before you run anything, before a test fails, before it reaches production. That is the whole value proposition.

## Inference: usually you do not need the annotation

TypeScript works out the type from the value, so the annotation above is redundant:

```typescript
let age = 36;            // inferred as number
age = "thirty-six";      // still an error
```

**Prefer inference.** Write the annotation when it adds information the value does not carry — a function's parameters and return type, an empty array, or a variable you declare before assigning.

```typescript
const items: string[] = [];      // without this it would be any[]
let selected: string | null = null;
```

:::tip `const` narrows further than `let`
```typescript
let a = "hello";      // type is string
const b = "hello";    // type is "hello" — the literal itself
```
A `const` cannot change, so TypeScript records the exact value. That is what makes literal types and discriminated unions work later.
:::

## The primitive types

```typescript
let n: number = 3.14;        // one type for ints and floats alike
let s: string = "text";
let b: boolean = true;
let big: bigint = 9007199254740993n;
let sym: symbol = Symbol("id");
let nothing: null = null;
let missing: undefined = undefined;
```

`null` and `undefined` are distinct. Roughly: `undefined` means "no value was ever set", `null` means "explicitly set to nothing". Under `strictNullChecks` (which you should have on) neither is assignable to other types unless you say so:

```typescript
let title: string = null;          // error under strict mode
let subtitle: string | null = null; // fine — you declared the possibility
```

## any, unknown and never

Three special types worth understanding early, because generated code reaches for the wrong one.

**`any` turns the checker off** for that value. It is contagious — anything derived from an `any` is also unchecked — and it is how a codebase quietly loses its type safety.

```typescript
let data: any = JSON.parse(raw);
data.wahtever.deeply.nested;      // no error. no help. typo ships.
```

**`unknown` is the safe version.** You can hold anything in it, but you must narrow before you use it:

```typescript
let data: unknown = JSON.parse(raw);
data.toUpperCase();                       // error — good
if (typeof data === "string") {
  data.toUpperCase();                     // fine, narrowed to string
}
```

**`never`** is the type with no values — a function that always throws, or a branch that cannot be reached. You will meet it mostly in exhaustiveness checks.

:::warn The one rule to carry forward
Use `unknown` where you are tempted to use `any`. It costs one narrowing check and it keeps the rest of your program honest. Turn on `noImplicitAny` (part of `strict`) so the compiler tells you when an `any` sneaks in.
:::

## Running it

TypeScript does not run directly — it is checked, then the types are stripped to produce JavaScript.

```bash
npm i -D typescript
npx tsc --init          # creates tsconfig.json
npx tsc                 # type check + emit .js
npx tsc --noEmit        # type check only — what CI should run
```

Modern Node can also run `.ts` files directly by stripping types without checking them, which is convenient in development. Checking and running are separate concerns: **running your code does not mean it type-checks.** Keep `tsc --noEmit` in your build.

## Exercise

```typescript
// Declare, with the right types:
//   - a constant `siteName` holding "Learn TypeScript"
//   - a variable `visitors` starting at 0 that you increment
//   - a variable `lastVisitor` that is a string OR null, starting null
// Then log all three.

// write your code here
```

## Common questions

### Should I annotate everything?

No — annotate where it adds information. Function parameters and return types are worth annotating because they are a contract other code depends on. Local variables with an obvious initialiser are better left inferred; a redundant annotation is one more thing to keep in sync.

### `interface` or `type` for object shapes?

Either. `type` handles unions and intersections uniformly and is the more common default now; `interface` supports declaration merging, which matters if you are extending third-party types. Pick one for your codebase and be consistent — the next lesson covers both.

### What happened to `var`?

It is function-scoped rather than block-scoped and it hoists, which produces bugs that `let` and `const` make impossible. It still works because TypeScript never breaks JavaScript, but there is no reason to write it.
