Skip to content
TypeScript for PHP Developers

TypeScript for PHP Developers

Al Amin Ahamed

Al Amin Ahamed

Senior Engineer

10 min read
𝕏 in

I spent eight years writing PHP before I wrote my first serious TypeScript. The mental model transfer is smoother than you'd expect — but there are enough sharp edges that a PHP developer coming to TypeScript fresh will waste time on avoidable mistakes. Here's the translation guide I wish I'd had.

The Basics Are Familiar

PHP's type system:

PHP
function greet(string $name, int $age): string
{
    return "Hello {$name}, you are {$age} years old.";
}

TypeScript:

TS
function greet(name: string, age: number): string {
    return `Hello ${name}, you are ${age} years old.`;
}

The syntax differs; the intent is identical.

null vs undefined — The Key Gotcha

PHP has null. TypeScript has both null and undefined — and they're different.

  • undefined — variable declared but never assigned
  • null — explicitly assigned "nothing"

In PHP, unset properties throw. In TypeScript, accessing an unset property returns undefined (no error). This burns every PHP developer at least once.

TS
const user = { name: 'Al Amin' };
console.log(user.email); // undefined — no error in JS!

Turn on strictNullChecks in tsconfig.json and TypeScript will catch this at compile time.

Optional Chaining

PHP's null-safe operator: $user?->address?->city

TypeScript's equivalent: user?.address?.city

They're syntactically identical in purpose.

Interfaces vs Types

PHP has interfaces as contracts for classes. TypeScript interfaces shape objects (data):

TS
interface Post {
    id: number;
    title: string;
    publishedAt: Date | null;
}

// type aliases work too — use interface for objects, type for unions
type Status = 'draft' | 'published' | 'archived';

PHP has no equivalent of union types in data (Status above). TypeScript's union types replace the PHP pattern of using string constants.

Generics

PHP:

PHP
/**
 * @template T
 * @param array<T> $items
 * @param callable(T): bool $fn
 * @return array<T>
 */
function filter(array $items, callable $fn): array
{
    return array_values(array_filter($items, $fn));
}

TypeScript:

TS
function filter<T>(items: T[], fn: (item: T) => boolean): T[] {
    return items.filter(fn);
}

TypeScript generics are first-class. You don't need PHPDoc workarounds.

Async / Await

PHP's synchronous model has no direct equivalent of promises. The closest mental model: think of async/await as Laravel's job queues, but inline in your code.

TS
async function fetchPost(id: number): Promise<Post> {
    const response = await fetch(`/api/posts/${id}`);
    return response.json();
}

// Calling it
const post = await fetchPost(42);

The await pauses execution of the current function until the promise resolves — other code continues running in the meantime. It's non-blocking I/O, not concurrency.

tsconfig.json Essentials

JSON
{
    "compilerOptions": {
        "strict": true,
        "target": "ES2020",
        "module": "ESNext",
        "moduleResolution": "bundler",
        "jsx": "react-jsx",
        "baseUrl": ".",
        "paths": { "@/*": ["resources/js/*"] }
    }
}

"strict": true enables all the checks PHP developers expect from a type system. Without it, TypeScript is optional and mostly useless.

Share 𝕏 in
Al Amin Ahamed

Al Amin Ahamed

Senior software engineer & AI practitioner. Laravel, PHP, WordPress plugins, WooCommerce extensions.

About me →

One email a month. No noise.

What I shipped, what I read, occasional deep dive. Unsubscribe anytime.