01: TypeScript to PHP - Type Systems Compared
TypeScript to PHP: Type Systems Compared
Section titled “TypeScript to PHP: Type Systems Compared”Overview
Section titled “Overview”Both TypeScript and modern PHP (8.0+) offer robust type systems, but they approach typing from different philosophies. TypeScript provides compile-time static typing that gets erased at runtime, while PHP offers runtime-checked static typing that validates types during execution.
In this chapter, we’ll map TypeScript’s type features to their PHP equivalents and explore the practical differences.
Learning Objectives
Section titled “Learning Objectives”By the end of this chapter, you’ll be able to:
- ✅ Compare TypeScript’s compile-time types to PHP’s runtime types
- ✅ Translate TypeScript type annotations to PHP
- ✅ Understand union types, intersection types, and nullable types in both languages
- ✅ Use strict typing modes in PHP
- ✅ Recognize when PHP’s type system is more permissive or restrictive than TypeScript’s
Code Examples
Section titled “Code Examples”📁 View Code Examples on GitHub
This chapter includes working code examples:
basic-types.php- Basic type annotations and demonstrationsnullable-union-types.php- Nullable types, union types, and advanced patterns
Clone the repository and run the examples:
git clone https://github.com/dalehurley/codewithphp.gitcd codewithphp/code/php-typescript-developers/chapter-01php basic-types.phpKey Concepts
Section titled “Key Concepts”The Fundamental Difference
Section titled “The Fundamental Difference”TypeScript:
// Type checking happens at compile-timefunction greet(name: string): string { return `Hello, ${name}!`;}
greet(123); // ❌ TypeScript error: Argument of type 'number' is not assignable to parameter of type 'string'After compilation, this becomes:
// All types are erasedfunction greet(name) { return `Hello, ${name}!`;}
greet(123); // ✅ Runs in JavaScript (no runtime checks)PHP:
<?phpdeclare(strict_types=1); // Enable strict type checking
function greet(string $name): string { return "Hello, {$name}!";}
greet(123); // ❌ PHP fatal error: Argument #1 must be of type string, int givenPHP’s types are checked at runtime. No compilation step needed, but type violations cause runtime errors.
Basic Type Annotations
Section titled “Basic Type Annotations”Primitive Types
Section titled “Primitive Types”| TypeScript | PHP | Notes |
|---|---|---|
string | string | Identical |
number | int or float | PHP distinguishes integers and floats |
boolean | bool | PHP uses bool, not boolean |
null | null | Identical |
void | void | Function returns nothing |
any | mixed | Accepts any type (PHP 8.0+) |
never | never | Function never returns (PHP 8.1+) |
unknown | N/A | No direct equivalent |
Examples Side-by-Side
Section titled “Examples Side-by-Side”TypeScript:
function add(a: number, b: number): number { return a + b;}
let result: number = add(5, 10);let message: string = "The result is " + result;let isValid: boolean = result > 0;PHP:
<?phpdeclare(strict_types=1);
function add(int $a, int $b): int { return $a + $b;}
$result = add(5, 10); // Type inference$message = "The result is " . $result;$isValid = $result > 0;Key Differences:
- PHP requires
$prefix for variables - PHP uses
.for string concatenation instead of+ - PHP’s type inference is limited (no explicit type annotation for variables in most cases)
Nullable Types
Section titled “Nullable Types”TypeScript Union with null
Section titled “TypeScript Union with null”function findUser(id: number): User | null { // Return User or null if not found}
let user: User | null = findUser(123);PHP Nullable Type
Section titled “PHP Nullable Type”<?phpfunction findUser(int $id): ?User { // Return User or null if not found}
$user = findUser(123); // $user is User or nullPHP Shorthand:
?string=string | null?int=int | null?User=User | null
Union Types
Section titled “Union Types”TypeScript
Section titled “TypeScript”function format(value: string | number): string { if (typeof value === 'number') { return value.toFixed(2); } return value.toUpperCase();}
format(42); // "42.00"format("hello"); // "HELLO"PHP (8.0+)
Section titled “PHP (8.0+)”<?phpdeclare(strict_types=1);
function format(string|int $value): string { if (is_int($value)) { return number_format($value, 2); } return strtoupper($value);}
format(42); // "42.00"format("hello"); // "HELLO"Differences:
- PHP uses
|without spaces (style convention) - PHP has
is_int(),is_string(), etc. instead oftypeof - PHP doesn’t have a unified
numbertype (useint|float)
Array Types
Section titled “Array Types”TypeScript
Section titled “TypeScript”// Array of stringslet names: string[] = ["Alice", "Bob"];let namesAlt: Array<string> = ["Alice", "Bob"]; // Generic syntax
// Tuple (fixed length, specific types)let person: [string, number] = ["Alice", 30];
// Object typelet user: { name: string; age: number } = { name: "Alice", age: 30};<?phpdeclare(strict_types=1);
// Array (no generic typing in native PHP)$names = ["Alice", "Bob"];
// Type hint: array of any typefunction processNames(array $names): void { foreach ($names as $name) { echo $name; }}
// No native tuple support, use array$person = ["Alice", 30]; // Not type-safe
// Associative array (like object)$user = [ 'name' => 'Alice', 'age' => 30];PHP Limitations:
- No built-in generic types (e.g.,
array<string>) - Use PHPStan or Psalm for generic annotations via docblocks:
/*** @param array<string> $names* @return array<int>*/function processNames(array $names): array {// ...}
Interfaces
Section titled “Interfaces”TypeScript
Section titled “TypeScript”interface User { id: number; name: string; email: string; isActive?: boolean; // Optional property}
function getUser(id: number): User { return { id: 1, name: "Alice", email: "alice@example.com" };}<?phpdeclare(strict_types=1);
interface User { public function getId(): int; public function getName(): string; public function getEmail(): string; public function isActive(): bool;}
class UserModel implements User { public function __construct( private int $id, private string $name, private string $email, private bool $isActive = false ) {}
public function getId(): int { return $this->id; }
public function getName(): string { return $this->name; }
public function getEmail(): string { return $this->email; }
public function isActive(): bool { return $this->isActive; }}Key Differences:
- TypeScript interfaces describe object shapes (structural typing)
- PHP interfaces define method contracts (nominal typing)
- PHP interfaces cannot have properties, only methods
- TypeScript interfaces can represent object literals; PHP requires classes
PHP Alternative (Object Shape): Use classes with public properties:
<?phpclass User { public function __construct( public int $id, public string $name, public string $email, public bool $isActive = false ) {}}
$user = new User(1, "Alice", "alice@example.com");echo $user->name; // "Alice"TypeScript
Section titled “TypeScript”enum Status { Pending = "pending", Approved = "approved", Rejected = "rejected"}
function updateStatus(status: Status): void { console.log(`Status: ${status}`);}
updateStatus(Status.Approved); // "Status: approved"PHP (8.1+)
Section titled “PHP (8.1+)”<?phpdeclare(strict_types=1);
enum Status: string { case Pending = 'pending'; case Approved = 'approved'; case Rejected = 'rejected';}
function updateStatus(Status $status): void { echo "Status: {$status->value}";}
updateStatus(Status::Approved); // "Status: approved"Similarities:
- Both support string and numeric backing values
- Both are strongly typed
- Both prevent invalid values
Differences:
- PHP uses
casekeyword instead of just property names - PHP accesses enum values with
->valueinstead of direct access - PHP enums can have methods (TypeScript enums cannot)
Type Assertions and Casting
Section titled “Type Assertions and Casting”TypeScript
Section titled “TypeScript”let value: unknown = "hello";let length: number = (value as string).length; // Type assertion<?php$value = "hello"; // mixed type$length = strlen((string) $value); // Type castingPHP Type Casting:
(int),(float),(string),(bool),(array),(object)- More permissive than TypeScript (may lose data)
Strict Mode
Section titled “Strict Mode”TypeScript
Section titled “TypeScript”{ "compilerOptions": { "strict": true, "strictNullChecks": true, "strictFunctionTypes": true }}<?phpdeclare(strict_types=1); // Must be first line of file
function add(int $a, int $b): int { return $a + $b;}
// Without strict_types:add("5", "10"); // ✅ Works, strings coerced to ints
// With strict_types:add("5", "10"); // ❌ Fatal error: must be of type int, string givenBest Practice:
Always use declare(strict_types=1) at the top of every PHP file. It’s the closest equivalent to TypeScript’s strict mode.
Readonly Properties
Section titled “Readonly Properties”TypeScript
Section titled “TypeScript”interface User { readonly id: number; name: string;}
let user: User = { id: 1, name: "Alice" };user.name = "Bob"; // ✅ OKuser.id = 2; // ❌ Error: Cannot assign to 'id' because it is a read-only propertyPHP (8.1+)
Section titled “PHP (8.1+)”<?phpclass User { public function __construct( public readonly int $id, public string $name ) {}}
$user = new User(1, "Alice");$user->name = "Bob"; // ✅ OK$user->id = 2; // ❌ Fatal error: Cannot modify readonly propertyType Juggling and Coercion
Section titled “Type Juggling and Coercion”One of the biggest differences between TypeScript and PHP is how they handle type coercion.
TypeScript Type Coercion
Section titled “TypeScript Type Coercion”// JavaScript's loose typing (TypeScript allows this)let value: any = "42";let doubled = value * 2; // 84 (string coerced to number)
// TypeScript with proper types prevents thislet typedValue: string = "42";let doubled2 = typedValue * 2; // ❌ Error: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum typePHP Type Juggling (Without Strict Types)
Section titled “PHP Type Juggling (Without Strict Types)”<?php// Without strict_types, PHP aggressively coerces typesfunction add(int $a, int $b): int { return $a + $b;}
echo add("10", "20"); // ✅ 30 (strings → ints)echo add("10.5", "20"); // ✅ 30 (floats truncated to ints)echo add("10", 2.5); // ✅ 12 (float truncated to int)echo add("hello", "5"); // ⚠️ 5 (non-numeric string → 0)PHP with Strict Types
Section titled “PHP with Strict Types”<?phpdeclare(strict_types=1);
function add(int $a, int $b): int { return $a + $b;}
echo add(10, 20); // ✅ 30echo add("10", "20"); // ❌ Fatal error: Argument must be of type int, string givenecho add(10, 2.5); // ❌ Fatal error: Argument must be of type int, float givenKey Insight: Without declare(strict_types=1), PHP’s type system behaves more like JavaScript with implicit coercion. Always enable strict types for TypeScript-like behavior.
Mixed and Never Types Explained
Section titled “Mixed and Never Types Explained”The mixed Type
Section titled “The mixed Type”TypeScript’s any:
let value: any = "hello";value = 42; // ✅ OKvalue = true; // ✅ OKvalue.anyMethod(); // ✅ TypeScript allows (runtime error possible)PHP’s mixed:
<?phpdeclare(strict_types=1);
function process(mixed $value): mixed { // $value can be any type if (is_string($value)) { return strtoupper($value); } if (is_int($value)) { return $value * 2; } return $value;}
echo process("hello"); // "HELLO"echo process(42); // 84echo process(true); // 1 (true converted to string)Differences from any:
- PHP’s
mixedis type-safe at boundaries (function signatures) - You must use type guards (
is_string(),is_int()) to narrow types - Unlike TypeScript’s
any,mixeddoesn’t disable type checking
The never Type
Section titled “The never Type”TypeScript:
// Function that never returns (throws or infinite loop)function throwError(message: string): never { throw new Error(message);}
function infiniteLoop(): never { while (true) { // Never exits }}PHP (8.1+):
<?phpdeclare(strict_types=1);
// Function that always throwsfunction throwError(string $message): never { throw new Exception($message);}
// Function that exitsfunction terminate(string $message): never { echo $message; exit(1);}
// ❌ This would be a type error:function invalid(): never { return 42; // Error: never type cannot return a value}Use Cases:
- Functions that always throw exceptions
- Functions that call
exit()ordie() - Exhaustiveness checking in match expressions
Literal Types
Section titled “Literal Types”TypeScript Literal Types
Section titled “TypeScript Literal Types”type Status = "pending" | "approved" | "rejected";type Port = 3000 | 8080 | 9000;
function setStatus(status: Status): void { console.log(`Status: ${status}`);}
setStatus("approved"); // ✅ OKsetStatus("invalid"); // ❌ ErrorPHP Literal Types (Limited)
Section titled “PHP Literal Types (Limited)”PHP doesn’t have literal types for primitives, but you can achieve similar results with enums:
<?phpdeclare(strict_types=1);
enum Status: string { case Pending = 'pending'; case Approved = 'approved'; case Rejected = 'rejected';}
function setStatus(Status $status): void { echo "Status: {$status->value}\n";}
setStatus(Status::Approved); // ✅ OKsetStatus('invalid'); // ❌ Fatal error: must be of type Status
// Enum with methods (PHP advantage!)enum Status: string { case Pending = 'pending'; case Approved = 'approved'; case Rejected = 'rejected';
public function isComplete(): bool { return $this === self::Approved || $this === self::Rejected; }
public function color(): string { return match($this) { self::Pending => 'yellow', self::Approved => 'green', self::Rejected => 'red', }; }}
$status = Status::Approved;echo $status->isComplete(); // trueecho $status->color(); // "green"Callable Types and Function Signatures
Section titled “Callable Types and Function Signatures”TypeScript Function Types
Section titled “TypeScript Function Types”// Function type annotationtype MathOperation = (a: number, b: number) => number;
const add: MathOperation = (a, b) => a + b;const multiply: MathOperation = (a, b) => a * b;
// Callback parameterfunction calculate(a: number, b: number, operation: MathOperation): number { return operation(a, b);}
calculate(5, 10, add); // 15calculate(5, 10, multiply); // 50PHP Callable Types
Section titled “PHP Callable Types”<?phpdeclare(strict_types=1);
// PHP uses 'callable' typefunction calculate(int $a, int $b, callable $operation): int { return $operation($a, $b);}
$add = fn(int $a, int $b): int => $a + $b;$multiply = fn(int $a, int $b): int => $a * $b;
echo calculate(5, 10, $add); // 15echo calculate(5, 10, $multiply); // 50
// More specific with PHPStan/Psalm annotations/** * @param callable(int, int): int $operation */function calculateTyped(int $a, int $b, callable $operation): int { return $operation($a, $b);}PHP 8.2+ First-Class Callable Syntax:
<?phpclass Calculator { public function add(int $a, int $b): int { return $a + $b; }}
$calc = new Calculator();$addFunction = $calc->add(...); // First-class callableecho $addFunction(5, 10); // 15Type Guards and Narrowing
Section titled “Type Guards and Narrowing”TypeScript Type Guards
Section titled “TypeScript Type Guards”function process(value: string | number): string { // Type guard with typeof if (typeof value === "number") { return value.toFixed(2); // TypeScript knows it's a number } return value.toUpperCase(); // TypeScript knows it's a string}
// User-defined type guardinterface Dog { bark(): void;}
interface Cat { meow(): void;}
function isDog(animal: Dog | Cat): animal is Dog { return (animal as Dog).bark !== undefined;}
function makeSound(animal: Dog | Cat): void { if (isDog(animal)) { animal.bark(); // TypeScript knows it's a Dog } else { animal.meow(); // TypeScript knows it's a Cat }}PHP Type Guards
Section titled “PHP Type Guards”<?phpdeclare(strict_types=1);
function process(string|int $value): string { // Type guard with is_*() functions if (is_int($value)) { return number_format($value, 2); } return strtoupper($value);}
// Interface-based type checkinginterface Dog { public function bark(): void;}
interface Cat { public function meow(): void;}
function makeSound(Dog|Cat $animal): void { // Use instanceof for object types if ($animal instanceof Dog) { $animal->bark(); } else { $animal->meow(); }}PHP Type Check Functions:
is_int(),is_float(),is_string(),is_bool(),is_array()is_null(),is_object(),is_resource(),is_callable()instanceoffor class/interface type checking
Practical Comparison
Section titled “Practical Comparison”| Feature | TypeScript | PHP |
|---|---|---|
| Type Checking | Compile-time | Runtime |
| Type Coercion | Moderate | Aggressive (without strict types) |
| Nullability | T | null | ?T |
| Union Types | string | number | string|int|float |
| Intersection Types | A & B | No native support |
| Literal Types | "pending" | "approved" | Use enums instead |
| Generics | Array<T>, Promise<T> | Docblock only (@param array<T>) |
| Interfaces | Structural | Nominal (method contracts) |
| Enums | ✅ (ES3+) | ✅ (PHP 8.1+, more powerful) |
| Readonly | ✅ | ✅ (PHP 8.1+) |
| Type Inference | Strong | Limited |
| Any Type | any | mixed |
| Never Type | never | never (PHP 8.1+) |
| Type Guards | typeof, custom guards | is_*(), instanceof |
| Callable Types | (a: T) => R | callable + docblocks |
| Strict Mode | tsconfig.json | declare(strict_types=1) |
Common Pitfalls and Gotchas
Section titled “Common Pitfalls and Gotchas”Pitfall 1: Forgetting declare(strict_types=1)
Section titled “Pitfall 1: Forgetting declare(strict_types=1)”<?php// ❌ BAD: No strict typesfunction greet(string $name): string { return "Hello, {$name}!";}
greet(123); // Silently converts 123 to "123" 🐛<?php// ✅ GOOD: Always use strict typesdeclare(strict_types=1);
function greet(string $name): string { return "Hello, {$name}!";}
greet(123); // Fatal error: must be of type stringPitfall 2: Array Type Hints Are Not Generic
Section titled “Pitfall 2: Array Type Hints Are Not Generic”// TypeScript: Type-safe arraysfunction processStrings(items: string[]): void { items.forEach(item => console.log(item.toUpperCase()));}
processStrings([1, 2, 3]); // ❌ Error: Type 'number' is not assignable to type 'string'<?phpdeclare(strict_types=1);
// PHP: array type hint accepts ANY arrayfunction processStrings(array $items): void { foreach ($items as $item) { echo strtoupper($item); // Runtime error if $item is not a string! }}
processStrings([1, 2, 3]); // ✅ Compiles, ❌ Runtime errorSolution: Use PHPStan annotations:
<?php/** * @param array<string> $items */function processStrings(array $items): void { foreach ($items as $item) { echo strtoupper($item); }}// PHPStan will catch type errors during static analysisPitfall 3: Float vs Int Distinction
Section titled “Pitfall 3: Float vs Int Distinction”// TypeScript: One numeric typefunction double(n: number): number { return n * 2;}
double(5); // ✅ 10double(5.5); // ✅ 11<?phpdeclare(strict_types=1);
function double(int $n): int { return $n * 2;}
double(5); // ✅ 10double(5.5); // ❌ Fatal error: must be of type int, float givenSolution: Use union types for numeric flexibility:
<?phpfunction double(int|float $n): int|float { return $n * 2;}
double(5); // ✅ 10double(5.5); // ✅ 11.0Pitfall 4: Nullable Return Types Must Be Explicit
Section titled “Pitfall 4: Nullable Return Types Must Be Explicit”// TypeScript: Return type inferred as User | undefinedfunction findUser(id: number) { return users.find(u => u.id === id);}<?phpdeclare(strict_types=1);
// ❌ BAD: Missing nullable return typefunction findUser(int $id): User { return $users[$id] ?? null; // Runtime error if null returned!}
// ✅ GOOD: Explicit nullable returnfunction findUser(int $id): ?User { return $users[$id] ?? null;}Pitfall 5: Property Type Must Match Constructor Assignment
Section titled “Pitfall 5: Property Type Must Match Constructor Assignment”// TypeScript: No issueclass User { name: string;
constructor(name: string | null) { this.name = name ?? "Anonymous"; }}<?php// ❌ BAD: Type mismatchclass User { public string $name;
public function __construct(?string $name) { $this->name = $name ?? "Anonymous"; // OK $this->name = null; // Fatal error: Cannot assign null to string }}
// ✅ GOOD: Matching typesclass User { public function __construct( public string $name = "Anonymous" ) {}}Pitfall 6: Void Functions Can Return Null
Section titled “Pitfall 6: Void Functions Can Return Null”// TypeScript: void means no return valuefunction log(message: string): void { console.log(message); return null; // ❌ Error: Type 'null' is not assignable to type 'void'}<?phpdeclare(strict_types=1);
// PHP: void allows explicit 'return;' or 'return null;'function log(string $message): void { echo $message; return null; // ✅ OK (but unnecessary)}
function logBetter(string $message): void { echo $message; return; // ✅ Better style}
function logBest(string $message): void { echo $message; // ✅ No return statement needed}Pitfall 7: Properties Without Defaults Must Be Initialized
Section titled “Pitfall 7: Properties Without Defaults Must Be Initialized”// TypeScript: Properties can be undefinedclass User { name: string; email?: string; // Optional}
let user = new User(); // OK, name is undefined<?php// ❌ BAD: Uninitialized typed propertyclass User { public string $name; public string $email;}
$user = new User(); // Fatal error: Typed property must not be accessed before initialization
// ✅ GOOD: Initialize in constructor or provide defaultsclass User { public function __construct( public string $name = "", public ?string $email = null ) {}}
$user = new User(); // ✅ OKPitfall 8: Type Declarations Are Per-File
Section titled “Pitfall 8: Type Declarations Are Per-File”<?phpdeclare(strict_types=1);
function add(int $a, int $b): int { return $a + $b;}<?php// ❌ No declare(strict_types=1)
require 'file1.php';
add("5", "10"); // ✅ Works! Coercion happens based on CALLER's strict_typesKey Insight: declare(strict_types=1) affects how the CURRENT file calls functions, not how functions in OTHER files behave. Always declare it in every file!
Best Practices for TypeScript Developers
Section titled “Best Practices for TypeScript Developers”- Always use
declare(strict_types=1)- First line after<?phpin every file - Use PHPStan or Psalm - Get compile-time-like type checking for arrays and generics
- Prefer explicit nullable types -
?Typeover implicit null returns - Use union types liberally -
int|floatinstead of justfloatwhen both should work - Leverage enums for literal types - More powerful than TypeScript enums
- Document array types - Use
@param array<Type>in docblocks - Use readonly for immutability - Equivalent to TypeScript’s
readonly - Avoid
mixedwhen possible - Be specific with union types instead - Use
neverfor functions that throw - Helps with exhaustiveness checks - Initialize all typed properties - No implicit
undefinedlike TypeScript
Hands-On Exercise
Section titled “Hands-On Exercise”Task 1: Convert TypeScript to PHP
Section titled “Task 1: Convert TypeScript to PHP”Given this TypeScript code:
interface Product { id: number; name: string; price: number; inStock: boolean;}
function calculateTotal(products: Product[]): number { return products.reduce((sum, p) => sum + p.price, 0);}
let products: Product[] = [ { id: 1, name: "Laptop", price: 999.99, inStock: true }, { id: 2, name: "Mouse", price: 29.99, inStock: true }];
console.log(calculateTotal(products)); // 1029.98Convert it to PHP. Try it yourself before checking the solution!
Solution
<?phpdeclare(strict_types=1);
class Product { public function __construct( public int $id, public string $name, public float $price, public bool $inStock ) {}}
/** * @param array<Product> $products */function calculateTotal(array $products): float { return array_reduce( $products, fn($sum, $p) => $sum + $p->price, 0.0 );}
$products = [ new Product(1, "Laptop", 999.99, true), new Product(2, "Mouse", 29.99, true)];
echo calculateTotal($products); // 1029.98Key Changes:
- Interface → Class with public properties
- Arrow function
(sum, p) => sum + p.price→fn($sum, $p) => $sum + $p->price reduce()→array_reduce()console.log()→echo- Added PHPStan docblock for array type safety
Task 2: Strict Types Challenge
Section titled “Task 2: Strict Types Challenge”What happens in PHP with and without declare(strict_types=1)?
<?php// Without strict_types (default)function double(int $n): int { return $n * 2;}
echo double("5"); // What gets printed?Answer
Without strict_types:
echo double("5"); // Prints: 10// PHP coerces "5" (string) to 5 (int) automaticallyWith strict_types:
<?phpdeclare(strict_types=1);
echo double("5"); // Fatal error: Argument #1 must be of type int, string givenLesson: Always use declare(strict_types=1) to avoid unexpected type coercion.
Key Takeaways
Section titled “Key Takeaways”- PHP’s type system is runtime-checked, unlike TypeScript’s compile-time checks
- Always use
declare(strict_types=1)- Without it, PHP behaves like JavaScript with aggressive type coercion - Type coercion without strict types is dangerous -
add("10", "20")works but leads to bugs - Nullable types:
?Typein PHP =Type | nullin TypeScript - Union types:
string|intin PHP =string | numberin TypeScript (but PHP distinguishes int/float) - No native generics in PHP; use PHPStan/Psalm
@param array<Type>docblocks for type-safe arrays - PHP interfaces are nominal (name-based), TypeScript interfaces are structural (shape-based)
- PHP enums (8.1+) can have methods, making them more powerful than TypeScript enums
mixedvsany: PHP’smixedis type-safe at boundaries, unlike TypeScript’sany- Type guards: Use
is_int(),is_string(),instanceofin PHP vstypeofin TypeScript - Callable types: PHP uses
callablekeyword; use docblocks for precise signatures - All typed properties must be initialized - No implicit
undefinedlike TypeScript strict_typesis per-file - Must declare in every file, not just once per project- Use PHPStan/Psalm - Essential for catching type errors before runtime (like tsc for TypeScript)
Next Steps
Section titled “Next Steps”Now that you understand the type systems, let’s explore modern PHP syntax that will feel familiar to you as a TypeScript developer.
Next Chapter: 02: Modern PHP Syntax for TS Developers
Resources
Section titled “Resources”- PHP Type System Documentation
- TypeScript Handbook
- PHPStan - PHP Static Analysis Tool
- Psalm - Static Analysis for PHP
Questions or feedback? Open an issue on GitHub