Skip to Content
TypeScript

TypeScript

Every primitive ships with types. Controlled/uncontrolled props narrow to the same value type, so you rarely need to annotate.

Generic value types

Primitives with a string value (Select, Tabs, RadioGroup, Accordion) accept a string literal type inferred from usage. You can narrow further with a generic union:

type Fruit = "apple" | "banana" | "cherry"; const [fruit, setFruit] = useState<Fruit>("apple"); <Select value={fruit} onValueChange={setFruit}> ... </Select>

onValueChange will infer Fruit here — no cast required.

Controlled vs uncontrolled props

Every stateful primitive exposes matched pairs: value + onValueChange, open + onOpenChange, checked + onCheckedChange. In TypeScript, you can pass either the controlled pair or the default* variant, but not both — the types are set up as a discriminated union.

Rendering as a different element

Some parts accept an asChild boolean that merges props onto their child instead of rendering their own element. TypeScript keeps the child’s ref types:

<Dialog.Trigger asChild> <Link href="/next">Go</Link> </Dialog.Trigger>

Behavior handlers and ARIA are still applied.

Last updated on