Skip to main content
Nonkera
Developer ToolsUpdated August 11, 2026

How to Convert a GraphQL Schema to TypeScript Types

How GraphQL's type system — nullability, lists, interfaces, and unions — maps onto TypeScript, and the nullable-by-default rule that trips up almost everyone converting a schema by hand.

Why a GraphQL schema and its TypeScript types drift apart

A GraphQL API and a TypeScript frontend describe the same data twice — once in the schema's SDL, and again in whatever interfaces a developer writes by hand to represent a response. The two start out matching and then quietly drift: a field gets added or its nullability changes in the schema, and the hand-written interface doesn't get updated to match, because nothing forces it to. The mismatch usually shows up later as a runtime bug rather than a compile error, since TypeScript can only be as accurate as the types it was given in the first place.

The rule that trips up almost everyone: nullable by default

In GraphQL SDL, a bare type name like String is nullable by default, and a trailing ! is what makes it required — the opposite of how most people instinctively read it coming from TypeScript, where a bare type is required unless you explicitly add a ?. field: String means the field can be null; field: String! means it can't. Converting by hand, it's easy to skim past the ! and get the nullability backwards, especially across a schema with dozens of fields where only some carry the mark.

The convention most tooling (including GraphQL Code Generator) settles on for the TypeScript side is to make a nullable field optional and explicitly include null in its type — field?: string | null — while a non-null field stays required with no null in its type — field: string. Matching that convention, rather than inventing your own, keeps generated and hand-written types consistent if you ever mix the two.

Lists compound the nullability, one level deeper

List types apply the same ! logic at two levels at once, which is where hand conversion gets genuinely error-prone. [String!]! is a required array of required strings — string[] in TypeScript, nothing nullable anywhere. [String] is a nullable array that may itself contain null entries — (string | null)[] | null. [String!]! and [String] look almost identical at a glance but produce meaningfully different TypeScript, and a schema with several list fields makes manual conversion a place where small mistakes compound quickly.

Interfaces, unions, and enums map over more directly

A GraphQL interface becomes a TypeScript interface, and a type's implements clause becomes extends — type Post implements Node becomes interface Post extends Node. A union type (union SearchResult = Author | Post) becomes a straightforward TypeScript union of the member names. Enums are the one place with a real choice to make: the modern convention is a string literal union (type Status = "DRAFT" | "PUBLISHED"), which is what most current GraphQL codegen tooling defaults to, though a traditional TypeScript enum works identically for the values themselves if that's what the rest of your codebase already uses.

When you need more than a quick conversion

A one-off schema-to-types conversion is genuinely useful for prototyping, reviewing a schema design, or building intuition for how the two type systems relate — but it isn't a substitute for a real codegen pipeline on an actively maintained API. Tools like GraphQL Code Generator introspect a live schema, regenerate types automatically whenever it changes, and can also generate fully typed query and mutation functions with field arguments included — none of which a static, one-off conversion can keep in sync over time. Reach for that kind of pipeline once a schema is actively evolving; reach for a quick conversion when you just need the shape right now.

Frequently asked questions

Is a GraphQL field nullable or required by default?

Nullable by default. A bare type name like String can be null; adding a trailing ! (String!) is what makes it required — the reverse of how a bare type behaves in TypeScript.

How do I represent [String!]! in TypeScript?

As string[] — a required array of required strings, since both exclamation marks (on the list itself and on its contents) remove nullability at that level.

Should GraphQL enums become a TypeScript enum or a string union?

A string literal union is the more common modern convention and what most GraphQL codegen tools default to, though a traditional TypeScript enum represents the same values just as validly if your codebase already prefers that style.

Do I need a live GraphQL server to generate matching types?

No — converting a schema definition (SDL) to TypeScript types is a text transformation that only needs the schema itself, not a running server. A live server is only needed for tooling that introspects a schema automatically or generates fully typed query functions.

How should a custom scalar like DateTime be typed?

Most conversions default a custom scalar to string, since that's the most common wire representation — worth double-checking against how your specific API actually serializes that scalar and adjusting by hand if it differs.

Nonkera uses cookies for analytics and to show ads. Every tool works exactly the same either way — see our Privacy Policy for details.