Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /Typescript
  4. /Any Unknown Never
Your Progress0%
0 of 30 completed

TypeScript Topics

Getting Started

  • What is TypeScript?
  • TypeScript vs JavaScript
  • Setting Up TypeScript
  • Your First TypeScript Program

Basic Types

  • Primitive Types
  • Arrays and Tuples
  • Objects and Type Aliases
  • Enums
  • Any, Unknown, and Never

Functions & Interfaces

  • Function Types and Signatures
  • Interfaces
  • Optional and Default Parameters
  • Function Overloading

Advanced Types

  • Union and Intersection Types
  • Type Guards and Narrowing
  • Literal Types
  • Type Assertions and Casting

Generics

  • Introduction to Generics
  • Generic Constraints
  • Utility Types

Classes & OOP

  • Classes and Constructors
  • Access Modifiers
  • Inheritance and Polymorphism
  • Abstract Classes and Interfaces

Practical TypeScript

  • Working with Modules
  • TypeScript with React
  • Async/Await and Promises
  • Working with APIs and JSON
  • Type Declarations and DefinitelyTyped
  • TypeScript Best Practices

Any, Unknown, and Never

Understanding TypeScript's special types for edge cases

TypeScript has three special types that handle unusual situations: any, unknown, and never. While regular types tell TypeScript exactly what a value is, these special types handle uncertainty, flexibility, and impossibility. Let's understand when and how to use each one safely!

The Any Type

The any type is an escape hatch—it disables type checking completely. A variable of type any can hold any value and you can do anything with it.

Basic Any Usage

any-basics.ts
// 'any' accepts any value
let value: any;

value = "hello";       // ✅ Works
value = 42;            // ✅ Works
value = true;          // ✅ Works
value = [1, 2, 3];     // ✅ Works
value = { name: "Yusuf" };  // ✅ Works
value = null;          // ✅ Works
value = undefined;     // ✅ Works

// Can perform any operation (no type checking)
value.toUpperCase();   // No error (might crash at runtime!)
value.length;          // No error
value();               // No error
value.anything;        // No error
value[0];              // No error

console.log(value);

When Any is Used

any-usage.ts
// 1. Migrating JavaScript to TypeScript
// Temporarily use 'any' while you figure out the real type
let legacyData: any = getLegacyData();

// 2. Third-party libraries without type definitions
// (Better: install @types package if available)
let externalLib: any = require("some-old-library");

// 3. Dynamic JSON data (better to use unknown)
let jsonData: any = JSON.parse(jsonString);

// 4. Rapidly prototyping
function quickTest(input: any) {
  console.log(input);
  // TODO: Add proper types later
}

// 5. Working with truly dynamic data
let dynamicConfig: any = loadDynamicConfig();

Important: Using any defeats the purpose of TypeScript! It removes all type safety and makes your code as unsafe as plain JavaScript. Avoid any unless you have a very good reason.

Problems with Any

any-problems.ts
// Problem: No type checking at all
let data: any = "hello";

// All these compile without errors but crash at runtime!
data.toFixed(2);           // Crash: string has no toFixed
data.push("item");         // Crash: string has no push
data.forEach(fn);          // Crash: string has no forEach

// Problem: Spreads to other variables
let text: any = 123;
let message: string = text;  // No error! But text is a number

// Problem: No autocomplete or IntelliSense
let user: any = { name: "Yusuf", age: 25 };
user.nam;  // No autocomplete, no typo detection
user.email;  // No error, but property doesn't exist

// The "any" infection spreads
function processAny(value: any) {
  return value.toString();  // Return type is 'any'
}

let result = processAny(123);
result.toUpperCase();  // No error, but might crash!

⚠️ Avoid Any!

  • any disables TypeScript's type checking
  • Errors are only caught at runtime, not compile-time
  • No autocomplete or IntelliSense
  • The "infection" spreads to other variables
  • Use unknown instead when type is uncertain

The Unknown Type

The unknown type is TypeScript's type-safe counterpart to any. Like any, it can hold any value, but unlike any, you must check the type before using it.

Any vs Unknown: The Safe Alternative

JavaScript (No Type Safety)
// JavaScript - no type checking at all
let data = getUserInput();

// Can do anything - might crash!
data.toUpperCase();
data.length;
data.split("");
data.forEach(item => console.log(item));
TypeScript (Type Safe)
// TypeScript with 'any' - bypasses type checking
let data: any = getUserInput();

// No errors, but still dangerous!
data.toUpperCase();    // Might crash
data.length;           // Might crash
data.split("");        // Might crash

// TypeScript with 'unknown' - forces type checking
let safeData: unknown = getUserInput();

// Must check type before using
if (typeof safeData === "string") {
  safeData.toUpperCase();  // ✅ Safe now
}

Why TypeScript is Better: 'any' disables type checking completely, while 'unknown' requires you to verify the type before use. Use 'unknown' for type-safe handling of uncertain data.

Key TypeScript Benefits

  • Type safety catches errors before runtime
  • Better IDE autocomplete and IntelliSense
  • Self-documenting code through types
  • Easier refactoring and maintenance
  • Fewer runtime errors in production

Basic Unknown Usage

unknown-basics.ts
// 'unknown' accepts any value
let value: unknown;

value = "hello";       // ✅ Works
value = 42;            // ✅ Works
value = true;          // ✅ Works
value = [1, 2, 3];     // ✅ Works
value = { name: "Yusuf" };  // ✅ Works

// But you CANNOT use it without checking type first
// value.toUpperCase();   // ❌ Error: Object is of type 'unknown'
// value.length;          // ❌ Error
// value();               // ❌ Error

// Must narrow the type first
if (typeof value === "string") {
  console.log(value.toUpperCase());  // ✅ Works - TypeScript knows it's a string
}

if (typeof value === "number") {
  console.log(value.toFixed(2));  // ✅ Works - TypeScript knows it's a number
}

if (Array.isArray(value)) {
  console.log(value.length);  // ✅ Works - TypeScript knows it's an array
}

Type Guards with Unknown

unknown-type-guards.ts
// Function that returns unknown
function parseJSON(jsonString: string): unknown {
  return JSON.parse(jsonString);
}

// Using type guards to safely handle unknown
let data: unknown = parseJSON('{"name": "Yusuf", "age": 25}');

// Type guard: Check if it's an object
if (typeof data === "object" && data !== null) {
  // Type assertion after validation
  const user = data as { name: string; age: number };
  console.log(`Name: ${user.name}, Age: ${user.age}`);
}

// Better: Create a type guard function
function isUser(value: unknown): value is { name: string; age: number } {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    "age" in value &&
    typeof (value as any).name === "string" &&
    typeof (value as any).age === "number"
  );
}

let apiData: unknown = parseJSON('{"name": "Amina", "age": 30}');

if (isUser(apiData)) {
  // TypeScript knows apiData is { name: string; age: number }
  console.log(`User: ${apiData.name}, Age: ${apiData.age}`);
  console.log(apiData.name.toUpperCase());  // ✅ Safe
}

Practical Example: API Response Handler

api-response-handler.ts
// Type for expected API response
type ApiResponse = {
  success: boolean;
  data: {
    id: number;
    name: string;
    email: string;
  };
};

// Function to validate API response
function isApiResponse(value: unknown): value is ApiResponse {
  if (typeof value !== "object" || value === null) {
    return false;
  }

  const obj = value as any;

  return (
    typeof obj.success === "boolean" &&
    typeof obj.data === "object" &&
    obj.data !== null &&
    typeof obj.data.id === "number" &&
    typeof obj.data.name === "string" &&
    typeof obj.data.email === "string"
  );
}

// Fetch and validate API data
async function fetchUser(id: number): Promise<ApiResponse> {
  const response = await fetch(`/api/users/${id}`);
  const data: unknown = await response.json();

  if (isApiResponse(data)) {
    return data;  // ✅ Type-safe
  }

  throw new Error("Invalid API response format");
}

// Usage
fetchUser(1)
  .then(response => {
    console.log(`User: ${response.data.name}`);
    console.log(`Email: ${response.data.email}`);
  })
  .catch(error => {
    console.error("Failed to fetch user:", error);
  });

Unknown with instanceof

unknown-instanceof.ts
// Using instanceof with unknown
function processValue(value: unknown): void {
  if (value instanceof Date) {
    console.log(`Date: ${value.toLocaleDateString()}`);
  } else if (value instanceof Error) {
    console.log(`Error: ${value.message}`);
  } else if (value instanceof Array) {
    console.log(`Array length: ${value.length}`);
  } else if (typeof value === "string") {
    console.log(`String: ${value.toUpperCase()}`);
  } else {
    console.log("Unknown type");
  }
}

processValue(new Date());           // "Date: 12/25/2024"
processValue(new Error("Oops"));    // "Error: Oops"
processValue([1, 2, 3]);            // "Array length: 3"
processValue("hello");              // "String: HELLO"
processValue(123);                  // "Unknown type"

✅ Use Unknown Instead of Any

When you don't know what type a value will be (like API responses or user input), use unknown instead of any. It forces you to validate the type before use, preventing runtime errors.

The Never Type

The never type represents values that never occur. It's used for functions that never return and for exhaustive type checking.

Functions That Never Return

never-functions.ts
// Function that throws always returns 'never'
function throwError(message: string): never {
  throw new Error(message);
  // No code after throw - function never returns normally
}

// Function with infinite loop returns 'never'
function infiniteLoop(): never {
  while (true) {
    console.log("Running forever...");
  }
  // Never reaches end
}

// Function that always exits process
function exitProcess(): never {
  process.exit(1);
  // Never returns
}

// Using never
function processData(data: string | null) {
  if (data === null) {
    throwError("Data cannot be null");
    // TypeScript knows this line is never reached
  }
  
  // TypeScript knows data is string here (null eliminated)
  console.log(data.toUpperCase());
}

Exhaustive Type Checking

never-exhaustive.ts
// Exhaustive checking with never
type Shape = 
  | { kind: "circle"; radius: number }
  | { kind: "square"; size: number }
  | { kind: "rectangle"; width: number; height: number };

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.size ** 2;
    case "rectangle":
      return shape.width * shape.height;
    default:
      // This ensures we handled all cases
      const exhaustiveCheck: never = shape;
      throw new Error(`Unhandled shape: ${exhaustiveCheck}`);
  }
}

// If we add a new shape type but forget to handle it:
type NewShape = 
  | { kind: "circle"; radius: number }
  | { kind: "square"; size: number }
  | { kind: "rectangle"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number };  // New!

// TypeScript will error in the default case because
// 'triangle' is not assignable to 'never'

Never vs Void

never-vs-void.ts
// void: Function returns (but returns nothing useful)
function logMessage(message: string): void {
  console.log(message);
  // Returns undefined implicitly
}

let voidResult = logMessage("Hello");  // undefined

// never: Function NEVER returns
function throwError(message: string): never {
  throw new Error(message);
  // Never reaches end, never returns
}

// This line is never reached:
// let neverResult = throwError("Error!");

// Comparison
function example1(): void {
  console.log("This function returns (with undefined)");
}  // Returns normally

function example2(): never {
  throw new Error("This function never returns");
}  // Never returns

// void can be assigned undefined
let v: void = undefined;  // ✅ Works

// never cannot be assigned any value
// let n: never = undefined;  // ❌ Error

Never in Union Types

never-unions.ts
// never is removed from union types
type Example1 = string | never;  // Result: string
type Example2 = number | never;  // Result: number
type Example3 = boolean | string | never;  // Result: boolean | string

// Practical use: Conditional types
type NonNullable<T> = T extends null | undefined ? never : T;

type Result1 = NonNullable<string | null>;  // string
type Result2 = NonNullable<number | undefined>;  // number
type Result3 = NonNullable<boolean | null | undefined>;  // boolean

// Filter out certain types
type FilterNumbers<T> = T extends number ? never : T;

type Result4 = FilterNumbers<string | number | boolean>;
// Result: string | boolean (number filtered out)

Practical Example: Safe Assertion

never-assertion.ts
// Create a function that asserts a condition
function assert(condition: boolean, message: string): asserts condition {
  if (!condition) {
    throwError(message);  // Returns never
  }
}

function throwError(message: string): never {
  throw new Error(message);
}

// Using assertion
function processUser(user: { name: string; age?: number }) {
  assert(user.age !== undefined, "User must have an age");
  
  // TypeScript now knows user.age is defined (not undefined)
  const nextYearAge = user.age + 1;  // ✅ No error
  console.log(`Next year: ${nextYearAge}`);
}

// Usage
processUser({ name: "Yusuf", age: 25 });     // Works
// processUser({ name: "Amina" });           // Throws error

Comparing Any, Unknown, and Never

Featureanyunknownnever
Can assign any value?✅ Yes✅ Yes❌ No
Type checking?❌ Disabled✅ RequiredN/A
Can use without checks?✅ Yes❌ No❌ No
Safe?❌ No✅ Yes✅ Yes
Common use caseMigration, legacy codeAPI responses, user inputFunctions that never return
type-comparison.ts
// any - no type safety
let anyValue: any = "hello";
anyValue.toFixed();  // Compiles, crashes at runtime

// unknown - type-safe
let unknownValue: unknown = "hello";
// unknownValue.toFixed();  // ❌ Error: must check type first

if (typeof unknownValue === "string") {
  unknownValue.toUpperCase();  // ✅ Safe
}

// never - represents impossibility
function neverReturns(): never {
  throw new Error("Never returns");
}

// Can't assign any value to never
// let neverValue: never = "hello";  // ❌ Error
// let neverValue: never = 123;      // ❌ Error

// never is assignable to everything (bottom type)
let str: string = neverReturns();  // ✅ Theoretical (never happens)
let num: number = neverReturns();  // ✅ Theoretical

Practical Examples

Example 1: Safe JSON Parsing

safe-json-parsing.ts
// Expected data structure
type UserData = {
  id: number;
  name: string;
  email: string;
  age: number;
};

// Type guard for validation
function isUserData(value: unknown): value is UserData {
  if (typeof value !== "object" || value === null) {
    return false;
  }

  const obj = value as any;

  return (
    typeof obj.id === "number" &&
    typeof obj.name === "string" &&
    typeof obj.email === "string" &&
    typeof obj.age === "number"
  );
}

// Safe JSON parser
function parseUserData(jsonString: string): UserData {
  const parsed: unknown = JSON.parse(jsonString);

  if (isUserData(parsed)) {
    return parsed;
  }

  throwError("Invalid user data format");
}

function throwError(message: string): never {
  throw new Error(message);
}

// Usage
try {
  const validJson = '{"id": 1, "name": "Yusuf", "email": "yusuf@example.com", "age": 25}';
  const user = parseUserData(validJson);
  console.log(`User: ${user.name}, Age: ${user.age}`);

  // This will throw an error
  const invalidJson = '{"id": 1, "name": "Amina"}';
  const invalidUser = parseUserData(invalidJson);
} catch (error) {
  console.error("Parse error:", (error as Error).message);
}

Example 2: Form Input Validation

form-validation.ts
// Handle user input (unknown type)
function validateFormInput(input: unknown): string {
  // Check if it's a string
  if (typeof input !== "string") {
    throwError("Input must be a string");
  }

  // Check if it's not empty
  const trimmed = input.trim();
  if (trimmed.length === 0) {
    throwError("Input cannot be empty");
  }

  // Check minimum length
  if (trimmed.length < 3) {
    throwError("Input must be at least 3 characters");
  }

  return trimmed;
}

function throwError(message: string): never {
  throw new Error(message);
}

// Validate email
function validateEmail(input: unknown): string {
  const value = validateFormInput(input);

  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!emailRegex.test(value)) {
    throwError("Invalid email format");
  }

  return value;
}

// Usage
try {
  const email1 = validateEmail("yusuf@example.com");
  console.log(`Valid email: ${email1}`);

  const email2 = validateEmail("invalid-email");  // Throws error
} catch (error) {
  console.error("Validation error:", (error as Error).message);
}

Example 3: API Error Handler

api-error-handler.ts
// API error types
type ApiError = {
  code: string;
  message: string;
  details?: unknown;
};

// Check if error is ApiError
function isApiError(error: unknown): error is ApiError {
  if (typeof error !== "object" || error === null) {
    return false;
  }

  const obj = error as any;
  return (
    typeof obj.code === "string" &&
    typeof obj.message === "string"
  );
}

// Handle API errors
function handleApiError(error: unknown): never {
  if (isApiError(error)) {
    console.error(`API Error [${error.code}]: ${error.message}`);
    if (error.details) {
      console.error("Details:", error.details);
    }
  } else if (error instanceof Error) {
    console.error(`Error: ${error.message}`);
  } else {
    console.error("Unknown error occurred:", error);
  }

  throw error;
}

// Make API call
async function fetchData(url: string): Promise<any> {
  try {
    const response = await fetch(url);

    if (!response.ok) {
      const error: ApiError = {
        code: `HTTP_${response.status}`,
        message: response.statusText
      };
      handleApiError(error);
    }

    return await response.json();
  } catch (error: unknown) {
    handleApiError(error);
  }
}

// Usage
fetchData("/api/users")
  .then(data => console.log(data))
  .catch(error => console.error("Caught:", error));

Test Your Knowledge

Type Safety Check

Which code is type-safe?

// Option A
let value: any = getUserInput();
console.log(value.toUpperCase());

// Option B
let value: unknown = getUserInput();
console.log(value.toUpperCase());

// Option C
let value: unknown = getUserInput();
if (typeof value === "string") {
  console.log(value.toUpperCase());
}

// Option D
let value: never = getUserInput();
console.log(value);

Common TypeScript Error

Code with Error
// Using 'any' - no type safety
function processData(data: any) {
  // Assumes data has these properties
  const name = data.name.toUpperCase();
  const age = data.age + 5;
  const email = data.email.toLowerCase();
  
  return { name, age, email };
}

// Runtime crash if data doesn't match expected shape!
processData({ name: "Yusuf" });  // Crashes: age is undefined
processData(null);               // Crashes: cannot read property
processData("string");           // Crashes: no properties

❌ TypeError: Cannot read property 'name' of null (or other runtime errors)

What's Wrong?

Using 'any' disables all type checking. The code compiles successfully but crashes at runtime with unexpected input. TypeScript can't help you catch these errors.

Corrected Code
// Using 'unknown' - type-safe
function processData(data: unknown) {
  // Type guard to validate structure
  if (
    typeof data === "object" &&
    data !== null &&
    "name" in data &&
    "age" in data &&
    "email" in data
  ) {
    const validData = data as { name: string; age: number; email: string };
    
    const name = validData.name.toUpperCase();
    const age = validData.age + 5;
    const email = validData.email.toLowerCase();
    
    return { name, age, email };
  }
  
  throw new Error("Invalid data structure");
}

// Type-safe: errors caught before runtime
const result = processData({ name: "Yusuf", age: 25, email: "yusuf@example.com" });

Solution: Use 'unknown' and validate the data structure before use

Best Practices

  1. Avoid 'any' whenever possible—it defeats TypeScript's purpose
  2. Use 'unknown' for values of uncertain type (API responses, user input)
  3. Always validate 'unknown' values with type guards before use
  4. Use 'never' for functions that never return normally
  5. Enable 'noImplicitAny' in tsconfig.json to catch accidental any
  6. Create type guard functions for complex validation logic
  7. Document why you use 'any' if you must use it
  8. Use exhaustive checking with 'never' for switch/case safety
best-practices-example.ts
// ❌ Avoid: Using 'any'
function badExample(data: any) {
  return data.value.toUpperCase();
}

// ✅ Good: Using 'unknown' with validation
function goodExample(data: unknown): string {
  if (
    typeof data === "object" &&
    data !== null &&
    "value" in data &&
    typeof (data as any).value === "string"
  ) {
    return (data as { value: string }).value.toUpperCase();
  }
  throw new Error("Invalid data structure");
}

// ✅ Good: Type guard function
function isValidData(data: unknown): data is { value: string } {
  return (
    typeof data === "object" &&
    data !== null &&
    "value" in data &&
    typeof (data as any).value === "string"
  );
}

function betterExample(data: unknown): string {
  if (isValidData(data)) {
    return data.value.toUpperCase();
  }
  throw new Error("Invalid data structure");
}

// ✅ Good: Using 'never' for exhaustive checks
type Status = "pending" | "approved" | "rejected";

function handleStatus(status: Status): string {
  switch (status) {
    case "pending":
      return "Processing...";
    case "approved":
      return "Approved!";
    case "rejected":
      return "Rejected!";
    default:
      const exhaustive: never = status;
      throw new Error(`Unhandled status: ${exhaustive}`);
  }
}

Key Takeaways

  • any disables type checking—avoid it unless migrating legacy code
  • unknown is the type-safe alternative to any
  • Always validate unknown values with type guards before use
  • never represents values that never occur
  • Functions that always throw or loop forever return never
  • Use never for exhaustive checking in switch statements
  • void means returns nothing useful, never means never returns
  • Create reusable type guard functions for complex validation
  • Enable noImplicitAny to catch accidental any usage
  • unknown forces you to think about type safety

What's Next?

Congratulations! You've completed all the basic types in TypeScript. You now understand primitive types, arrays, tuples, objects, enums, and the special types. Next, we'll explore Function Types and Signatures! You'll learn how to type function parameters, return values, optional parameters, rest parameters, and more. Functions are where TypeScript really shines!

Get ready to master type-safe functions and make your code even more robust!

Mastering TypeScript's special types: any, unknown, and never!

Previous
Enums
Next
Function Types and Signatures

Never Miss a New TypeScript Tutorial

Join 1,000+ developers learning TypeScript step-by-step. Get new tutorials, type safety tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

TypeScript Tutorials

0 of 30 completed

Your Progress0%

Topics

Getting Started

  • What is TypeScript?
  • TypeScript vs JavaScript
  • Setting Up TypeScript
  • Your First TypeScript Program

Basic Types

  • Primitive Types
  • Arrays and Tuples
  • Objects and Type Aliases
  • Enums
  • Any, Unknown, and Never

Functions & Interfaces

  • Function Types and Signatures
  • Interfaces
  • Optional and Default Parameters
  • Function Overloading

Advanced Types

  • Union and Intersection Types
  • Type Guards and Narrowing
  • Literal Types
  • Type Assertions and Casting

Generics

  • Introduction to Generics
  • Generic Constraints
  • Utility Types

Classes & OOP

  • Classes and Constructors
  • Access Modifiers
  • Inheritance and Polymorphism
  • Abstract Classes and Interfaces

Practical TypeScript

  • Working with Modules
  • TypeScript with React
  • Async/Await and Promises
  • Working with APIs and JSON
  • Type Declarations and DefinitelyTyped
  • TypeScript Best Practices
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo