Modern Web Development in 2026
A practical series about building faster, cleaner, more maintainable web applications without chasing every shiny thing.
TypeScript won the argument.
Most serious JavaScript codebases either use it already or are planning around it. The more interesting question is what we do after adoption.
Because a lot of TypeScript code is still just JavaScript wearing a blazer.
function createUser(data: any): any {
return api.post("/users", data);
}
Technically TypeScript. Spiritually not.
Types should protect boundaries
The best places to spend type effort are boundaries:
- API input,
- API output,
- database records,
- form state,
- configuration,
- feature flags,
- permissions,
- domain events.
Internal implementation types are useful. Boundary types are where bugs go to die.
Replace vague shapes with domain language
This is common:
type User = {
id: string;
role: string;
status: string;
};
It is better than nothing, but it leaves too much room.
type UserRole = "admin" | "editor" | "viewer";
type UserStatus = "invited" | "active" | "suspended";
type User = {
id: UserId;
role: UserRole;
status: UserStatus;
};
Now invalid states have fewer places to hide.
Use branded types for dangerous strings
Not every string is the same kind of string.
type Brand<T, Name extends string> = T & { readonly __brand: Name };
type UserId = Brand<string, "UserId">;
type OrgId = Brand<string, "OrgId">;
Now this mistake becomes harder:
function getUser(orgId: OrgId, userId: UserId) {}
A plain string can come from anywhere. A branded string says, βThis value passed through a specific gate.β
Prefer discriminated unions over boolean soup
Boolean-heavy state is where UI bugs breed.
type RequestState = {
loading: boolean;
error?: string;
data?: User;
};
This allows nonsense:
{ loading: true, error: "Failed", data: user }
Use a union:
type RequestState =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: User }
| { status: "error"; message: string };
Now the impossible states are actually impossible.
Validate runtime data
TypeScript does not validate JSON at runtime.
This is one of the most expensive misunderstandings in web development.
const user = (await response.json()) as User;
That line does not make the response a User. It makes the compiler quiet.
Use runtime validation at external boundaries:
const UserSchema = z.object({
id: z.string(),
role: z.enum(["admin", "editor", "viewer"]),
status: z.enum(["invited", "active", "suspended"])
});
const user = UserSchema.parse(await response.json());
Now TypeScript and runtime reality are connected.
Use satisfies for configuration
Configuration should be checked without losing literal types.
const routes = {
home: "/",
dashboard: "/dashboard",
settings: "/settings"
} satisfies Record<string, `/${string}`>;
This catches invalid values while preserving useful inference.
Avoid type theater
Some types add noise but not safety.
type StringOrNumber = string | number;
type Callback = (...args: any[]) => any;
type ObjectMap = Record<string, any>;
These can be valid in rare cases, but often they are placeholders for decisions nobody made.
Good TypeScript is not more TypeScript. Good TypeScript is better constraints.
The practical checklist
Use this before merging TypeScript-heavy changes:
- [ ] Are external inputs validated at runtime?
- [ ] Are domain concepts named explicitly?
- [ ] Are impossible UI states impossible?
- [ ] Are IDs and tokens distinguishable?
- [ ] Are
anyand unsafe assertions isolated? - [ ] Can a new developer understand the model from the types?
- [ ] Do the types reduce tests you would otherwise need?
Final thought
TypeScript is not valuable because it makes JavaScript look professional.
It is valuable because it lets you encode decisions before those decisions become bugs.
If your types do not protect boundaries, model the domain, or remove impossible states, you are leaving most of the value on the table.
Sources
- InfoQ, βState of JavaScript 2025: Survey Reveals a Maturing Ecosystem with TypeScript Cementing Dominance,β published March 20, 2026: https://www.infoq.com/news/2026/03/state-of-js-survey-2025/
- Syncfusion, βFrontend Development Trends 2026,β published April 28, 2026: https://www.syncfusion.com/blogs/post/frontend-development-trends
Thanks for reading.
You can find me here:
Top comments (0)