# TypeScript Types Demystified: Simple Types, Special Types, and Type Inference

> Source: <https://dev.to/ramesh_s_a8f0867d239e927c/typescript-types-demystified-simple-types-special-types-and-type-inference-5bf0>
> Published: 2026-06-19 03:22:28+00:00

*In the first post, we covered why TypeScript exists and how to write your first program. Now it's time to get comfortable with the type system itself — the foundation everything else is built on.*

By the end of this post, you'll know how to type variables, arrays, and function parameters correctly. You'll also understand the "special" types that trip up most beginners: `any`

, `unknown`

, `never`

, and `void`

.

TypeScript's basic types map directly to JavaScript's primitives:

``` js
// string
let firstName: string = "Ramesh";
let greeting: string = `Hello, ${firstName}`;

// number (no separate int/float — it's all number)
let age: number = 31;
let price: number = 9.99;
let hex: number = 0xFF;

// boolean
let isLoggedIn: boolean = true;
let hasAccess: boolean = false;
```

These are the types you'll use most often. Simple, predictable, and exactly what you'd expect.

You don't always have to write the type. TypeScript **infers** it from the value you assign:

``` js
let city = "Chennai";    // TypeScript infers: string
let year = 2026;         // TypeScript infers: number
let isActive = true;     // TypeScript infers: boolean
```

Once inferred, that type is locked in:

``` js
let city = "Chennai";
city = 42; // ❌ Error: Type 'number' is not assignable to type 'string'
```

**Rule of thumb:** Let TypeScript infer types for local variables. Write explicit annotations for function parameters and return types.

``` js
// Let inference work for variables
const scores = [95, 87, 72]; // inferred as number[]

// Be explicit for function signatures
function calculateAverage(scores: number[]): number {
  return scores.reduce((a, b) => a + b, 0) / scores.length;
}
// ✅ Explicit annotation — good for function params & return types
function formatName(first: string, last: string): string {
  return `${first} ${last}`;
}

// ✅ Inferred — good for simple variable assignments
const result = formatName("Ramesh", "Kumar"); // inferred as string

// ❌ Over-annotating — redundant when value makes it obvious
const count: number = 5; // The `= 5` already tells TypeScript it's a number
```

**Arrays** hold multiple values of the same type:

``` js
// Two equivalent syntaxes
let tags: string[] = ["typescript", "javascript", "react"];
let scores: Array<number> = [95, 87, 72];

// TypeScript will catch wrong types in arrays
tags.push(42); // ❌ Error: Argument of type 'number' is not assignable to type 'string'
```

**Tuples** are fixed-length arrays where each position has a specific type:

``` js
// A tuple: exactly [string, number]
let user: [string, number] = ["Ramesh", 31];

// Order and types are both enforced
let wrongOrder: [string, number] = [31, "Ramesh"]; // ❌ Error

// Accessing tuple elements
console.log(user[0]); // "Ramesh" — TypeScript knows this is string
console.log(user[1]); // 31 — TypeScript knows this is number
```

Tuples are great for things like coordinate pairs, key-value pairs, or when returning multiple values from a function:

```
function getMinMax(numbers: number[]): [number, number] {
  return [Math.min(...numbers), Math.max(...numbers)];
}

const [min, max] = getMinMax([3, 1, 7, 2, 9]);
// min: number, max: number — fully typed!
```

Sometimes a value can legitimately be more than one type. That's what union types handle:

```
// This function accepts string OR number
function formatId(id: string | number): string {
  return `ID-${id}`;
}

formatId("abc123"); // ✅
formatId(42);       // ✅
formatId(true);     // ❌ Error: boolean not in the union
```

Union types are especially useful for API responses, form inputs, and optional values:

``` js
// A status that can only be one of these three strings
let orderStatus: "pending" | "shipped" | "delivered";

orderStatus = "shipped";   // ✅
orderStatus = "cancelled"; // ❌ Error: not in the union

// A value that might not exist yet
let userId: number | null = null;
userId = 101; // Fine, it's assigned now
```

`any`

, `unknown`

, `never`

, `void`

These four confuse most beginners. Here's each one explained clearly.

`any`

— The Escape Hatch (Use Sparingly)
`any`

turns off TypeScript's type checking for that value:

``` js
let data: any = "hello";
data = 42;        // Fine
data = true;      // Fine
data.foo.bar();   // Fine — no error, even if this blows up at runtime!
```

When to use it: Almost never. `any`

defeats the purpose of TypeScript. It's there as a last resort when migrating old JavaScript code.

```
// ❌ Overusing any — you've lost all type safety
function processData(input: any): any { ... }

// ✅ Be specific whenever possible
function processData(input: string[]): number { ... }
```

`unknown`

— The Safe Version of `any`

`unknown`

says "this could be anything, but I need to check before I use it":

``` js
let userInput: unknown = getUserInput();

// ❌ Can't use unknown directly
console.log(userInput.toUpperCase()); // Error!

// ✅ Must narrow the type first
if (typeof userInput === "string") {
  console.log(userInput.toUpperCase()); // Now it's safe
}
```

**Prefer unknown over any** when you genuinely don't know the type. It forces you to handle the uncertainty explicitly.

`void`

— Functions That Return Nothing
Use `void`

when a function doesn't return a value:

```
function logMessage(message: string): void {
  console.log(message);
  // No return statement needed
}

// Trying to use the return value of a void function makes no sense
const result = logMessage("hello"); // result is void — useless
```

`never`

— Code That Never Completes
`never`

represents values that never occur. It's used for:

```
// Always throws — never returns
function throwError(message: string): never {
  throw new Error(message);
}

// Exhaustive check — if you add a new status and forget to handle it,
// TypeScript will error here
function handleStatus(status: "active" | "inactive"): string {
  if (status === "active") return "User is active";
  if (status === "inactive") return "User is inactive";

  // This line should be unreachable
  const _exhaustive: never = status; // ❌ Error if you missed a case
  return _exhaustive;
}
```

`never`

is an advanced concept — don't worry if it doesn't fully click yet. You'll encounter it naturally as you write more TypeScript.

``` js
// Primitives
let name: string = "Ramesh";
let age: number = 31;
let active: boolean = true;

// Arrays
let tags: string[] = ["ts", "js"];
let ids: number[] = [1, 2, 3];

// Tuples
let point: [number, number] = [10, 20];

// Union
let id: string | number = "abc";
let status: "on" | "off" = "on";

// Special types
let anything: any = "...";     // Avoid when possible
let safeAny: unknown = "...";  // Safer — must narrow before use
function log(): void { ... }   // No return value
function fail(): never { throw new Error(); } // Never returns
```

The TypeScript type system is deep, but the everyday usage is straightforward. Here's what to take away:

`string`

, `number`

, `boolean`

cover most cases`string[]`

or `Array<string>`

, your choice`any`

`unknown`

`void`

`never`

Found this helpful? Follow for the rest of the series. Questions or corrections? Drop them in the comments.
