- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
TypeScript for PHP Developers: The Parts That Actually Matter
About Post
You type a parameter as number. The compiler is happy. And at runtime, adding a fee of 5 to 100 gives you "1005". If you come from PHP, that feels like a betrayal: you wrote the type, so why didn't anything enforce it?
If you come from modern PHP, TypeScript feels familiar very quickly: types on parameters, interfaces, union types, nullable values. But one difference sits underneath everything, and once you understand it, the rest of the language makes sense.
The big one: PHP checks at runtime, TypeScript checks before
In PHP, types are enforced while the code runs:
declare(strict_types=1);
function addFee(float $amount): float
{
return $amount + 5;
}
addFee('100'); // TypeError, thrown at runtime
TypeScript checks types when you compile, and then erases them. The JavaScript that actually runs has no types at all:
function addFee(amount: number): number {
return amount + 5;
}
addFee('100'); // compile error, good
const body = JSON.parse(input); // type: any
addFee(body.amount); // compiles fine, returns "1005" if amount is a string
The compiler trusted that body.amount was a number because any switches checking off. Nothing checked at runtime, because nothing exists at runtime to check. That's the "1005" bug.
So the mental model is: TypeScript types are promises you make to the compiler. Inside your code, the compiler holds you to them very well. At the edges, where data comes from outside (API responses, JSON, form input, local storage), nobody holds the outside world to them.
Validate at the boundary
This is the habit PHP developers need to bring with them. In Laravel you validate requests before using them. In TypeScript, do the same with incoming data. A library like Zod lets you declare a schema once and get both the runtime check and the type:
import { z } from 'zod';
const Contract = z.object({
id: z.number(),
reference: z.string(),
endsAt: z.string().nullable(),
});
type Contract = z.infer<typeof Contract>;
const contract = Contract.parse(await res.json()); // throws if the shape is wrong
Writing await res.json() as Contract instead looks similar, but it's just a promise again. as checks nothing.
Interfaces describe shapes, not classes
In PHP, a class satisfies an interface only if it says implements. TypeScript uses structural typing: if an object has the right shape, it fits, no declaration needed.
interface HasEmail {
email: string;
}
function sendWelcome(user: HasEmail) { /* ... */ }
const tenant = { email: '[email protected]', unit: 'A-12' };
sendWelcome(tenant); // fine: it has an email, no "implements" needed
sendWelcome({ email: '[email protected]', unit: 'A-12' }); // error, see below
The quirk in that last line: passing an object literal directly with extra properties is flagged (an "excess property check"), because an unexpected property written inline is usually a typo. The same object in a variable is fine. In practice you'll use interfaces (or type aliases, which are nearly interchangeable for object shapes) mostly to describe data, not to build class hierarchies.
Union types, with superpowers
PHP has had union types since 8.0: int|string. TypeScript has them too, plus literal types and narrowing: after a check, the compiler knows which member you have. Combine those and you get discriminated unions, one of TypeScript's best features:
type PaymentResult =
| { status: 'paid'; receiptId: string }
| { status: 'failed'; reason: string }
| { status: 'pending' };
function describe(result: PaymentResult): string {
switch (result.status) {
case 'paid':
return `Paid, receipt ${result.receiptId}`;
case 'failed':
return `Failed: ${result.reason}`;
case 'pending':
return 'Still processing';
}
}
Inside case 'paid', accessing result.reason is a compile error. Add a fourth status later and the function stops compiling until you handle it. The closest PHP equivalent is an enum with a match, but PHP can't attach different fields to each case like this.
Generics: the thing PHP doesn't have
PHP has no native generics. You write @template in docblocks and let PHPStan or Psalm check them. In TypeScript they're part of the language:
interface Paginated<T> {
data: T[];
currentPage: number;
lastPage: number;
}
async function getJson<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<T>; // a promise to the compiler, not a check
}
const page = await getJson<Paginated<Contract>>('/api/contracts');
Now page.data[0].reference autocompletes and is type-checked everywhere. Notice the comment, though: the generic is only as honest as the data. That's the boundary rule again.
Two kinds of nothing
PHP has null. JavaScript has null and undefined. Missing object properties and unset variables are undefined; APIs and databases usually give you null. In TypeScript, reference?: string means "might be missing", while reference: string | null means "always present, might be null". The good news: ?. and ?? work just like PHP's ?-> and ??.
Turn on strict mode. Day one.
Set "strict": true in tsconfig.json. Without it, null can sneak into any type and untyped parameters silently become any, which throws away most of the value. Adding strict mode to an existing project later is much more painful than starting with it.
And prefer unknown over any. Both accept any value, but unknown forces you to check before using it, while any turns the type checker off.
If you remember one thing: PHP types protect you at runtime. TypeScript types protect you while you write code. Neither protects you from bad data at the edges, so validate there in both languages.
Cheat sheet
| PHP | TypeScript |
|---|---|
| Types checked at runtime | Types checked at compile time, then erased |
implements required | Matching shape is enough |
int|string | number | string, plus literal types like 'paid' |
?string | string | null, or prop?: string for optional |
| Generics via PHPStan docblocks | Built-in generics |
mixed | unknown (safe) or any (unchecked) |
| Form Request validation | Schema validation (Zod or similar) at the boundary |
The TypeScript Handbook is genuinely good, and with a PHP background you can skim the first half quickly.
If you've made the move from PHP to TypeScript, what tripped you up first? My bet is on trusting as more than it deserves.

Be first to comment it...