Hire TypeScript developers
TypeScript is JavaScript with a type system layered on top, and hiring for it means judging whether someone uses types to describe a system or to silence a compiler.
What TypeScript actually is
TypeScript is a language developed by Microsoft that adds static types to JavaScript. It compiles to plain JavaScript and adds nothing at runtime: every type disappears before the code executes. That single fact explains most of what is useful and most of what is misunderstood about it. The types are a conversation with the compiler and with the next developer, not a guarantee about what arrives from the network.
It has become the default for new commercial JavaScript work, to the point where hiring a JavaScript developer without TypeScript is now the unusual case rather than the standard one. Most major libraries ship type definitions, most frameworks assume it, and most teams of more than three people find the refactoring confidence worth the cost of annotation.
The important distinction when hiring is between someone who writes TypeScript and someone who models with it. The first group adds annotations until errors stop. The second uses the type system to make invalid states impossible to express, so that whole categories of bug become compile failures rather than production incidents. The difference is visible within ten minutes of looking at code and is almost invisible on a CV.
The part that separates seniors from mid-levels
The type system is structural, not nominal. Two types with the same shape are interchangeable regardless of what they are called, which is why a function expecting a User will happily accept any object that happens to have the same fields. This is powerful and it is also the reason that naming a type does not protect you. Developers who have come from Java or C# frequently expect nominal behaviour and are surprised, and the pattern they reach for when they want genuine distinctness, usually a branded type, is a good thing to ask about.
Types vanish at runtime, and this is the source of the most consequential mistake in real codebases. Declaring that an API response is of a certain type does not check anything. If the server sends something else, the code runs with a wrong assumption and fails somewhere far from the cause. The mature pattern is to validate at the boundary with a schema library and derive the type from the schema, so that the type and the runtime check cannot drift apart. A candidate who does this unprompted is telling you they have debugged the alternative.
The third thing that separates levels is knowing when to stop. The type system is expressive enough to encode remarkably complex constraints, and it is entirely possible to write types that nobody on the team can read or modify. Senior judgement here looks like restraint: strong types at boundaries, simple types in the middle, and a willingness to accept a slightly looser type rather than a clever one that will outlive the person who wrote it.
Where TypeScript is used
The label “TypeScript developer” covers several jobs that share a technology and little else. These are the settings the work usually turns up in, and the one you are hiring into should shape the whole process, because the judgement each demands is different.
Front-end applications
React, Angular and Vue codebases where props, state and API responses are typed and refactoring confidence is the main payoff.
Node.js services
Back-end services where types are shared with the front end, removing a whole class of integration mismatch.
Shared libraries and SDKs
Packages consumed by other teams, where the type definitions are the public documentation and a breaking type change is a breaking change.
Monorepos
Codebases where types cross package boundaries and the build graph has to keep them consistent.
Migration work
Large JavaScript codebases being converted file by file, usually the largest single category of TypeScript work on the market.
Tooling and build systems
Internal developer tooling where the authors are also the users and the type system is used aggressively.
If a candidate's experience sits in a different row of that list from the work you have, that is not a reason to reject them, but it is the thing to probe. Ask what would be different about their approach in your setting. Someone who can answer that has transferable judgement. Someone who says it would be much the same has probably not thought about it.
The toolchain around it
Nobody hires for TypeScript alone. The surrounding tools are where most of the day-to-day work happens, and a gap in any of them costs more time than a gap in the core library. This is the set that turns up most often on real job specifications alongside it.
- Zod or Valibot
- Runtime validation at boundaries with types derived from the schema, closing the gap types leave open.
- ESLint with the TypeScript plugin
- Rules the compiler does not enforce, particularly around unsafe any usage and floating promises.
- A build tool
- Vite, esbuild or the compiler itself. Most teams type-check and transpile separately for speed.
- Vitest or Jest
- Testing, with type-level assertions where a public API contract matters.
- tsconfig strict settings
- The single highest-value configuration decision in a TypeScript project.
- Turborepo or Nx
- Monorepo orchestration where types are shared across packages.
- Prisma or Drizzle
- Database access with types generated from the schema rather than hand-written.
- tRPC or generated API clients
- Type safety across the network boundary without hand-maintaining two definitions.
Related skills that frequently appear on the same specification: Node.js, React, Next.js, Angular, Vue.js.
What to test in an interview
These are the topics that separate candidates in practice. Each one is given with why it discriminates, what a strong answer sounds like, and the response that should make you slow down. None of them requires a whiteboard.
What happens to types at runtime
The foundation. Someone who is unclear here will write code that trusts data it should not.
- Strong answer: States plainly that types are erased, and describes validating at the boundary with a schema that the type is derived from.
- Warning sign: Believes a type assertion on an API response provides any guarantee.
any, unknown and type assertions
How someone handles uncertainty reveals their whole approach.
- Strong answer: Uses unknown and narrows it, treats any as debt to be recorded, and can explain when an assertion is legitimate.
- Warning sign: Uses any routinely, or asserts to make an error disappear without establishing why it appeared.
Modelling invalid states out of existence
The difference between annotating code and designing with types.
- Strong answer: Reaches for discriminated unions so that impossible combinations cannot be expressed, and can give an example from their own work.
- Warning sign: Models everything as an interface with optional fields.
Strictness settings and migration
Most real TypeScript work is on a codebase that was not born strict.
- Strong answer: Knows what strict null checks change, and has migrated incrementally with strictness on for new files.
- Warning sign: Has only worked on projects where someone else configured it, or turned strictness off to progress.
Generics, and when not to use them
Tests both capability and restraint.
- Strong answer: Writes generics for genuinely reusable code and can describe a time they simplified an over-engineered type.
- Warning sign: Either avoids generics entirely, or writes deeply nested conditional types for ordinary problems.
Types across the network boundary
Where front end and back end silently disagree.
- Strong answer: Generates types from a schema or shares them through a typed client, and knows the failure mode when the two drift.
- Warning sign: Hand-maintains matching interfaces on both sides and has not noticed the problem.
Compiler performance on a large codebase
Only comes up when someone has worked at scale.
- Strong answer: Knows about project references and incremental builds, and has diagnosed a slow type-check.
- Warning sign: Has never seen type-checking take long enough to matter.
Warning signs in a TypeScript codebase
The fastest way to read a candidate is to ask what they have found wrong in code they inherited. These are the patterns that come up most often, what they cost, and what fixing them looks like. A developer who recognises three or four of these from their own experience is worth more than one who can recite the documentation.
Assertion instead of validation
- What you see: API responses cast to a type with no runtime check.
- What it costs: Failures appear far from their cause, usually as an undefined property deep in unrelated code.
- The fix: Parse at the boundary with a schema library and derive the type from it, so the two cannot diverge.
any as an escape hatch
- What you see: any scattered through the codebase, often with a comment promising to fix it.
- What it costs: Type safety stops at each one, and errors propagate silently through everything downstream.
- The fix: Use unknown and narrow explicitly. Where any is genuinely necessary, isolate it behind a typed wrapper.
Interfaces with everything optional
- What you see: A type where most fields are marked optional so it fits every case.
- What it costs: Every consumer has to handle every absence, and the type no longer describes anything.
- The fix: Split into a discriminated union of the states that actually occur.
Type gymnastics in application code
- What you see: Deeply nested conditional and mapped types in ordinary business logic.
- What it costs: Nobody but the author can change it, and errors become unreadable.
- The fix: Keep advanced types in libraries and boundaries. In application code, prefer the simpler type.
Duplicated types across the boundary
- What you see: The same shape hand-written in both the client and the server.
- What it costs: They drift, and nothing detects it until production.
- The fix: Generate from one source, or share a package. The compiler should catch the mismatch.
Non-null assertions used routinely
- What you see: The exclamation mark applied whenever strict null checking complains.
- What it costs: Exactly the runtime errors that strict null checking existed to prevent.
- The fix: Narrow properly. If a value genuinely cannot be null, express why in the type rather than overriding the check.
What each level can own
Job titles are not comparable between companies, so it is more useful to describe levels by what a person can be left to own without supervision. These are the boundaries we use when we assess a TypeScript developer.
- Junior
- Writes annotated code in an established codebase. Needs review on boundaries, and will reach for any under pressure.
- Mid-level
- Designs types for a feature, validates at boundaries, and uses unions deliberately. Can read a complex compiler error.
- Senior
- Owns the type architecture for an area: what is shared, what is generated, how strict the settings are, and where runtime validation sits. Knows when a simpler type is the better answer.
- Staff
- Owns the type contract between teams and services, the migration path for the codebase's strictness, and the compiler performance budget.
How the work is usually scoped
Team shape follows the kind of work, not the headcount you happen to have budget for. These are the shapes that come up most often and the constraint that actually governs each one.
JavaScript to TypeScript migration
- Usual team: One senior developer to set the pattern, then the existing team.
- What governs it: Governed by file count and test coverage. Done incrementally, it never needs to stop delivery.
Shared type package for a monorepo
- Usual team: One senior developer.
- What governs it: Scoped by how many consumers exist and how much they currently duplicate.
Typing an existing API boundary
- Usual team: One developer with access to both sides.
- What governs it: Fast when the API has a schema, slow when the contract only exists in people's heads.
Strictness uplift
- Usual team: One developer, time-boxed and incremental.
- What governs it: Measured in error count reduced per week. Turning strict on everywhere at once is the way this fails.
SDK or public library
- Usual team: One senior developer plus a reviewer who is a consumer.
- What governs it: The types are the documentation, so the review must come from someone using it rather than writing it.
Migration work you may actually be hiring for
A large share of TypeScript work is not new development. It is moving an existing system from one state to another while it stays in service. These are the migrations that come up most often, and each one asks for a different kind of experience from the person you hire.
JavaScript to TypeScript
- Why teams do it: Refactoring confidence and fewer integration mismatches, with the benefit scaling by team size.
- What to watch: Migrate file by file with both allowed in the build. Hold new files to strict settings from day one, and record each escape hatch as debt rather than leaving it to be forgotten.
Hand-written API interfaces to generated or shared types
- Why teams do it: Two hand-maintained definitions of the same contract always drift, and nothing detects it until production.
- What to watch: Generation is only as good as the schema behind it. Where the API has no schema, writing one is the real project and typing is the easy part.
Loose settings to strict mode
- Why teams do it: Strict null checking prevents the single largest category of runtime error in JavaScript applications.
- What to watch: Enable per directory or per file rather than globally. A repository-wide flip produces thousands of errors and the effort is usually abandoned.
Type assertions at boundaries to schema validation
- Why teams do it: Assertions promise something the compiler cannot check; schemas actually check it.
- What to watch: Start with the boundaries that have caused incidents. Doing every boundary at once is a large change with no obvious moment to stop.
Migration work rewards a different temperament from greenfield work. The useful question in an interview is not whether someone has done the specific migration you face, but whether they have ever run one incrementally: behind a flag, with both paths live, and with a way back. Developers who have only done big-bang cutovers tend to propose them again.
What a good brief for this role contains
Most of the time lost in hiring a TypeScript developer is lost before anyone is interviewed, in the gap between what the brief says and what the team actually needs. These are the points that, for this technology specifically, change who the right candidate is. A brief that answers them can be matched in days. One that does not produces a shortlist that looks reasonable and converts badly.
- Whether the codebase is already TypeScript, partly migrated, or plain JavaScript. These are three different jobs.
- What the strictness settings are, because a loose codebase and a strict one demand different habits.
- Whether types cross a network or package boundary, and how they are kept in step if so.
- Whether runtime validation exists at API boundaries, or whether introducing it is part of the work.
- Whether the person will follow an existing type architecture or be expected to set one.
- Build and type-check times if they are currently a complaint, since that is specialist work rather than general TypeScript work.
If you cannot answer some of these yet, that is normal and it is still worth writing down which ones are open. An unknown that is named can be worked around. An unknown that is papered over in a job specification turns into a rejected shortlist and a restart four weeks later.
What the US market pays for this work
TypeScript work is counted by the US Bureau of Labor Statistics under Software Developers. That classification is broader than the technology itself, so treat the figures as the shape of the market a TypeScript developer is hired into rather than as a rate card for the skill. Across the United States the Bureau counts 1,687,890 people in this occupation, with a median annual wage of $135,980.
The spread matters more than the midpoint. The 90th percentile is about 2.6 times the 10th, which is a wide band for a single occupation and tells you that the title on its own carries very little pricing information. Two people described as a TypeScript developer can sit at $82,460 and $214,670 in the same national dataset. When a budget is set from a median without asking which end of that range the work actually needs, the hire that follows is usually the wrong one in one direction or the other.
Related classifications are worth reading alongside it, because teams hiring for TypeScript frequently end up recruiting against these titles too:
| Occupation | Employed | 25th percentile | Median | 75th percentile | 90th percentile |
|---|---|---|---|---|---|
| Software Developers | 1,687,890 | $105,210 | $135,980 | $171,980 | $214,670 |
| Web Developers | 70,190 | $64,230 | $92,650 | $126,230 | $162,290 |
Source: BLS Occupational Employment and Wage Statistics, May 2025. Figures cover all US employers and are not FuturByte rates.
These are employer-side wage figures for people on a US payroll. They exclude employer taxes, benefits, recruitment cost and the months a seat sits empty, all of which are real and none of which appear in a salary line. The useful way to read the table is as the cost of the alternative you are comparing against, not as a number to match.
How US metro markets compare for this role
The same job is priced very differently across the country. Ranked by median annual wage for Software Developers, the gap between the highest and lowest of the 28 metro areas covered here is a factor of about 1.7. San Jose sits at the top with a median of $213,110; Pittsburgh sits at the bottom with $124,500. A budget built from a national median will be wrong in both of those markets, in opposite directions.
| Metro area | Employed | Median wage | vs US median | Location quotient |
|---|---|---|---|---|
| San Jose, CA | 87,350 | $213,110 | +57% | 7.09 |
| San Francisco, CA | 69,030 | $186,640 | +37% | 2.68 |
| Seattle, WA | 92,770 | $167,280 | +23% | 4.10 |
| New York, NY | 121,000 | $166,830 | +23% | 1.17 |
| Boston, MA | 42,310 | $166,090 | +22% | 1.44 |
| San Diego, CA | 20,610 | $163,270 | +20% | 1.23 |
| Los Angeles, CA | 55,540 | $160,920 | +18% | 0.82 |
| Portland, OR | 18,260 | $156,000 | +15% | 1.39 |
| Washington, D.C. | 69,060 | $154,930 | +14% | 2.03 |
| Baltimore, MD | 16,850 | $138,900 | +2% | 1.14 |
| Denver, CO | 27,010 | $137,610 | +1% | 1.55 |
| Charlotte, NC | 20,820 | $135,920 | 0% | 1.41 |
| Chicago, IL | 40,370 | $134,380 | -1% | 0.82 |
| Austin, TX | 31,960 | $134,120 | -1% | 2.28 |
| Dallas-Fort Worth, TX | 67,030 | $133,290 | -2% | 1.52 |
| Philadelphia, PA | 28,480 | $133,040 | -2% | 0.91 |
| Atlanta, GA | 36,300 | $132,960 | -2% | 1.16 |
| Raleigh, NC | 12,580 | $132,770 | -2% | 1.56 |
| Miami, FL | 18,900 | $132,650 | -2% | 0.62 |
| Phoenix, AZ | 29,380 | $131,750 | -3% | 1.14 |
| Minneapolis-St. Paul, MN | 27,410 | $130,920 | -4% | 1.29 |
| Detroit, MI | 24,870 | $130,760 | -4% | 1.20 |
| Tampa, FL | 14,230 | $130,450 | -4% | 0.91 |
| Orlando, FL | 13,440 | $129,620 | -5% | 0.88 |
| Salt Lake City, UT | 19,040 | $129,600 | -5% | 2.12 |
| Houston, TX | 22,940 | $129,440 | -5% | 0.64 |
| Kansas City, MO | 12,160 | $124,990 | -8% | 1.02 |
| Pittsburgh, PA | 10,320 | $124,500 | -8% | 0.85 |
Location quotient compares how concentrated this occupation is in the metro against the national average. A value above 1 means the metro has more of this work than its size would predict.
The location quotient column is the more useful one for hiring. A high median tells you what a role costs; a high quotient tells you whether the people exist. San Jose, San Francisco, Seattle, Washington, D.C., Denver, Austin each have a quotient of 1.5 or above, meaning the work is concentrated there well beyond what the size of the local economy would predict. Those are the markets where a search is likely to be quick and competitive at the same time, and where a counter-offer is most likely to take a candidate off the table late in the process.
The opposite case is worth planning for too. In a metro with a low quotient, the total pool is small even when wages look reasonable, so the realistic options are to widen the search radius, accept a longer time to hire, or bring the capability in from outside the local market entirely. That last option is what most teams are weighing when they come to us.
Hiring risks worth naming
Every one of these has produced a bad hire somewhere. They are written down so that the process tests for them deliberately rather than discovering them in month three.
Annotation without modelling. Ask them to design a type for a state machine with several states. Optional fields everywhere is the answer to watch for.
No runtime validation habit. Ask what happens if the API returns something unexpected. This is the most consequential gap in the TypeScript market.
Over-engineering the type system. Ask about a type they simplified. Restraint is harder to teach than capability.
Only greenfield strict experience. Most real work is migration. Ask how they would introduce strictness to a large untyped codebase without stopping delivery.
Hiring TypeScript developers by metro area
Wages for this occupation vary more between US metro areas than most budget models assume. Each page below sets out the published employment and wage figures for that market, how it compares with the national picture, and what the local industry mix means for the kind of TypeScript developer who will be available.
- New York, NY $166,830 median
- Seattle, WA $167,280 median
- San Jose, CA $213,110 median
- Washington, D.C. $154,930 median
- San Francisco, CA $186,640 median
- Dallas-Fort Worth, TX $133,290 median
- Los Angeles, CA $160,920 median
- Boston, MA $166,090 median
- Chicago, IL $134,380 median
- Atlanta, GA $132,960 median
- Austin, TX $134,120 median
- Phoenix, AZ $131,750 median
- Philadelphia, PA $133,040 median
- Minneapolis-St. Paul, MN $130,920 median
- Denver, CO $137,610 median
- Detroit, MI $130,760 median
- Houston, TX $129,440 median
- Charlotte, NC $135,920 median
- San Diego, CA $163,270 median
- Salt Lake City, UT $129,600 median
- Miami, FL $132,650 median
- Portland, OR $156,000 median
- Baltimore, MD $138,900 median
- Tampa, FL $130,450 median
- Orlando, FL $129,620 median
- Raleigh, NC $132,770 median
- Kansas City, MO $124,990 median
- Pittsburgh, PA $124,500 median
Frequently asked questions
Should we migrate our JavaScript codebase to TypeScript?
If more than two or three people work in it and it is expected to live for years, almost certainly yes. The payoff is refactoring confidence and fewer integration bugs, and it scales with team size rather than with codebase size. The migration can be done file by file with both languages coexisting, so it does not require stopping delivery.
Do types make our application safer at runtime?
No, and this is the most expensive misunderstanding in TypeScript. Types are erased before the code runs. They catch mistakes in code you wrote; they do nothing about data arriving from a network, a file or a user. Runtime safety requires validation at the boundary, and the good pattern is to derive the type from that validation schema so the two cannot drift apart.
Is a TypeScript developer different from a JavaScript developer?
Increasingly not, in the sense that most commercial JavaScript work is now TypeScript. What does differ is depth. Someone who has only ever added annotations to satisfy a build is a different proposition from someone who designs with the type system, and only the second reliably reduces the bug rate.
How long does it take a JavaScript developer to become productive in TypeScript?
A competent JavaScript developer writes useful TypeScript within days. Designing good types takes months, and the gap shows up in code review rather than in delivery speed. If you are hiring for a codebase with an established type architecture, the ramp is short. If they will be setting that architecture, hire for it explicitly.
How strict should our settings be?
As strict as the codebase can currently bear, with new files held to the full standard from the start. Strict null checking is the setting that delivers most of the value and causes most of the initial errors. Turning it on across a large existing codebase in one pass is the way these efforts die.
Our build is slow. Is TypeScript the problem?
Sometimes, and it is usually fixable. Most teams get the largest win by separating type-checking from transpilation, so builds do not wait for the compiler. After that, project references on a monorepo and removing a few pathological types usually account for the rest. It is worth measuring before assuming.
Can TypeScript be shared between our front end and back end?
Yes, and it is one of the strongest arguments for using it on both sides. Sharing the types for an API contract removes an entire class of bug where the two sides quietly disagree. The mechanism matters less than the discipline of having exactly one definition rather than two that look alike.
What does bad TypeScript look like in review?
Any used as a shortcut, assertions on data that came from outside, interfaces where nearly every field is optional, and types complex enough that the error messages become unreadable. All four are visible quickly and all four predict a codebase that will be harder to change than an untyped one would have been.