TypeScript Developers

Hire TypeScript Developers Whose Types Catch Real Mistakes

Almost every JavaScript developer now says they know TypeScript. The useful question is narrower: do the types in their code prevent bugs, or do they exist to stop the compiler complaining. We test for the first kind.

  • Assessed on type design, not syntax
  • 2–3 profiles in 3 business days
  • Replacement free inside two weeks

TypeScript Is Not a Role. Read This Before You Write the Brief.

We could pretend otherwise and it would probably convert better, but a TypeScript developer is not a thing you hire in isolation. TypeScript sits on top of something else. On the frontend that is React, on the backend it is Node, on mobile it is React Native. Somebody with immaculate type discipline and no idea how your rendering or your data layer behaves is not going to be useful in your standup.

So treat TypeScript as a requirement laid across the role rather than as the role itself. Tell us the underlying stack and tell us how much the type discipline matters, and we will weight the shortlist accordingly. The rest of this page is about what that weighting actually looks like when we assess someone.

The Dividing Line

Types That Do Work Versus Types That Keep Quiet

Both codebases compile. Only one of them tells you about the bug before your users do.

Quieting the compiler

The escape hatch any appears wherever there was friction, so the error goes away and the wrongness stays. Assertions are used to tell the compiler it is mistaken rather than to narrow a type honestly. Optional properties multiply because making a field required somewhere would have meant fixing six call sites. A response from the network is asserted into a shape that nobody ever checked.

The result is a project with the cost of TypeScript and almost none of the benefit: slower builds, more ceremony, and runtime errors on properties the types promised were there.

Designing with types

Untrusted data is validated where it enters the system and the type is derived from that validation, so it is earned rather than claimed. States that cannot coexist are modelled so that they cannot be constructed. Function signatures are precise enough that the wrong argument order will not compile. Assertions exist, are rare, and carry a comment explaining what the compiler cannot see.

The benefit shows up in review and in refactoring: rename a field and the compiler lists every place that has to change, instead of you finding them in production one at a time.

Two habits, not two skill levels. Plenty of otherwise strong developers have only ever worked the left-hand way.

What We Look For

The Five Things That Tell Us Someone Can Design Types

Discriminated Unions

Most domain modelling problems are really a state machine wearing a plain object. A request is loading, or it succeeded with data, or it failed with an error — never two of those at once. Modelled as a union with a tag, the compiler stops you reading data that does not exist yet. Modelled as four optional fields, it will not, and someone will eventually render an error and a result on the same screen.

Generics With Restraint

Generics are for keeping a relationship between input and output so the caller does not lose information. They are not a scoring system. We look for someone who can write a reusable helper that still infers cleanly at the call site, and who knows when a plain concrete type is clearer than a clever one. Type gymnastics that only the author can maintain is a liability on a team.

Inference Over Annotation

Annotating everything is as much of a smell as annotating nothing. Good TypeScript leans on inference for locals and return values, and reserves explicit annotations for the boundaries that are meant to be contracts: exported functions, module interfaces, the shape of a public API. Knowing which is which is the judgement we are testing.

Typing an API Boundary Properly

Data from the network is unknown until something checks it. Declaring the response type and moving on is the single most common way a typed codebase still crashes. The right pattern is validation at the edge, with the type derived from the validator, so that a backend change produces a loud failure at the boundary instead of an undefined three components later.

Reading the Error Instead of Escaping It

Long TypeScript errors are intimidating and mostly informative. We watch what a candidate does with one: read from the bottom, find the actual mismatch, work out which side is wrong. Or reach for an assertion and move on. This one behaviour predicts more about the codebase they will leave behind than anything on the profile.

Knowing What Types Cannot Do

Types disappear at runtime. They do not validate user input, they do not stop a database returning null for a column marked required, and they do not protect an endpoint. A developer who understands the boundary between compile time and runtime writes the check where it is actually needed. One who does not will assume the type is a guarantee.

Strictness

What Turning the Strict Flags On Actually Costs

Worth doing. Not worth doing all at once on a Monday in a codebase with two hundred files.

What you gain

The bugs the compiler can find for you

Null and undefined checking is where most of the real value is: the property read on something that was not there, the callback that can be handed nothing, the array index that pretends it always returns a value. Turning it on surfaces bugs that were already in the product, not new ones.

How it is done sanely

One flag at a time, by module

Raise a single flag, fix the code you touch most often first, and stop new files from regressing. Measure progress in modules cleaned rather than errors remaining, because the error count moves in unhelpful directions while you work.

The honest cost

A quarter of low-grade friction

Expect more ceremony around external libraries with weak type definitions, some noisy narrowing, and a period where reviews argue about types more than behaviour. It settles. Teams that abandon strictness usually did so in the first three weeks.

What goes wrong

Everything on, then everything ignored

Flip every flag at once, generate four thousand errors, add a suppression comment at the top of the worst files and declare it finished. Now the configuration says strict, the code is not, and nobody trusts either. We have seen this more often than the successful version.

If the migration itself is the job, it is fixed-scope work rather than a hire, and often runs alongside a code audit.

Across the Boundary

Sharing Types Between Client and Server

The best and the worst version of this idea look almost identical in a pull request.

Ways that hold up

  • One source of truth — a schema, an API definition, a database model — with types generated from it
  • Validation at the edge, with the type inferred from the validator so the two cannot disagree
  • A shared package for domain types, versioned deliberately rather than imported across a repository boundary by accident
  • A typed client generated from the server contract, regenerated in CI so drift fails the build

Ways that drift

  • The same interface hand-copied into two repositories, kept in agreement by memory
  • A shared type that describes a response nobody validates, so it is a wish rather than a contract
  • Database models exported straight to the client, which leaks internal fields and makes every schema change a frontend change
  • A shared package that quietly couples deployments, so the frontend cannot ship without the backend
Fixed-scope API work
Vetting

How We Test TypeScript, Specifically

Model a domain that has impossible states in it

A small feature where certain combinations must never occur. We are looking for a union with a discriminant rather than a bag of optional fields, and for the candidate to notice when the requirements themselves are contradictory.

Fix a real error rather than escape it

A genuinely confusing compiler error in code they did not write. Reading it properly and correcting the underlying mismatch is the pass. Reaching for an assertion within the first minute is the thing we are screening out.

Review a diff that compiles and is wrong

Assertions hiding a nullable value, a response typed but never validated, a generic that has quietly widened to accept anything. We want to know which ones they flag, which they let through, and how they phrase the comment to a colleague.

Talk through a strictness decision

Given a large half-typed codebase and one quarter, what do they turn on first and what do they deliberately leave for later. There is no single right answer, and the reasoning is the whole point.

Briefing Us

What to Put in a TypeScript Brief

Tell us this

  • The underlying role first: React, Node, React Native or both sides
  • How much of the codebase is typed today, and whether strict is on
  • Whether types cross the client and server boundary, and how
  • Whether you want someone to raise the standard or simply match it
  • Build tooling, because that is often where a migration stalls
  • The first three tickets and the overlap hours you need
Compare engagement models

Skip this

  • Asking for a TypeScript developer without naming the stack underneath it
  • Type-puzzle interview questions that no one writes in production code
  • Expecting a new hire to migrate a large codebase between tickets
  • Treating strict mode as a box to tick rather than a programme with a cost
All roles and services
FAQ

Hiring TypeScript Developers

Is TypeScript a role on its own?

Not really, and we would rather say so than sell you a title. TypeScript sits on top of something: React on the frontend, Node on the backend, React Native on mobile. What you are actually hiring is a React or Node developer whose type discipline you can rely on. Tell us the underlying stack as well as the TypeScript requirement, because matching on the language alone produces a shortlist that is worse, not better.

How do you tell good type design from types added to silence the compiler?

We look at what happens at the edges. Someone with real discipline validates untrusted input where it arrives and lets the type follow from the validation, models a state machine so the impossible combinations cannot be constructed, and uses assertions rarely and with a comment saying why. The other pattern is easy to spot: any sprinkled at every point of friction, assertions used to override the compiler rather than to inform it, and a codebase that type-checks cleanly while still crashing on undefined.

Do we need strict mode turned on?

You want to end up there, but turning every strict flag on at once in a large codebase produces thousands of errors and a team that stops looking at them. The workable approach is to enable strictness one flag at a time, fix the modules you touch most first, and stop new code from regressing. Null checking is usually the flag that finds the most real bugs and also causes the most initial noise.

Should the client and server share types?

Where they can, yes, and it removes a whole category of silent mismatch. The important detail is that a shared type is a promise about data that arrives over a network, and the compiler cannot enforce it at runtime. Either generate the types from one source of truth such as a schema or an API definition, or validate the payload at the boundary so the type is earned rather than asserted. Hand-copied types on both sides drift and then lie.

How do you actually test TypeScript skill?

With a codebase that compiles and is still wrong. We ask candidates to model a domain where several states are possible but some combinations are not, write a function generic enough to be reused without losing inference at the call site, and fix a real error message rather than escape it. We also give them a diff full of assertions and see whether they can explain which ones are load-bearing and which are covering a genuine bug.

Can a developer you place migrate our JavaScript project to TypeScript?

Yes, and that work usually goes better as a fixed-scope engagement than as a background task inside a normal sprint. A migration has an order to it: build pipeline first, then types at the boundaries, then the modules everything imports, with strictness raised in stages. Done as an afterthought between tickets it tends to stall halfway, which is the worst of both worlds.

Rates are on the pricing page. Legacy work is covered under legacy modernisation.

Tell Us the Stack and the Standard

Name the underlying role, say how typed the codebase is today, and we will send two or three developers whose type discipline matches what you need.