Typedora UI
Utils

Typed Utils

Utility types for extracting option types and values from typed components.

Introduction

typed-utils provides utility types that power the type inference in all Typedora UI components. These types are automatically installed as a dependency when you add any typed component.

Key features:

  • Extract option types from flat or grouped options arrays
  • Extract value types based on custom value keys
  • Full support for as const arrays with literal types

Installation

npx shadcn@latest add https://typedora-ui.netlify.app/r/typed-utils.json
pnpm dlx shadcn@latest add https://typedora-ui.netlify.app/r/typed-utils.json
npx shadcn@latest add https://typedora-ui.netlify.app/r/typed-utils.json
bunx --bun shadcn@latest add https://typedora-ui.netlify.app/r/typed-utils.json

Note

This package is automatically installed when you add any typed component like TypedSelect, TypedCombobox, or TypedRadioGroup.

Usage

ExtractOption

ExtractOption extracts the individual option type from an options array. It handles both flat options and grouped options.

import type {  } from "@/lib/typed-utils";

// Flat options
const  = [
  { : "apple", : "Apple" },
  { : "banana", : "Banana" },
] as ;

type Fruit = <typeof >;
type Fruit = {
    readonly value: "apple";
    readonly label: "Apple";
} | {
    readonly value: "banana";
    readonly label: "Banana";
}
// Grouped options const = [ { : "Tropical", : [ { : "mango", : "Mango" }, { : "pineapple", : "Pineapple" }, ], }, ] as ; type GroupedFruit = <typeof >;
type GroupedFruit = {
    readonly value: "mango";
    readonly label: "Mango";
} | {
    readonly value: "pineapple";
    readonly label: "Pineapple";
}

ExtractOptionValue

ExtractOptionValue extracts the value type from options based on a specified key. This is useful when you need to type a variable that holds the selected value.

import type {  } from "@/lib/typed-utils";

// With default "value" key
const  = [
  { : "apple", : "Apple" },
  { : "banana", : "Banana" },
  { : "orange", : "Orange" },
] as ;

type FruitValue = <typeof , "value">;
type FruitValue = "apple" | "banana" | "orange"
// With custom "id" key const = [ { : 1, : "Alice" }, { : 2, : "Bob" }, { : 3, : "Charlie" }, ] as ; type UserId = <typeof , "id">;
type UserId = 3 | 1 | 2

Real-world Example

Here's how you might use these utility types in a real application:

import {  } from "react";
import type {  } from "@/lib/typed-utils";
import {  } from "@/components/typed-select";

const  = [
  { : "pending", : "Pending", : "yellow" },
  { : "approved", : "Approved", : "green" },
  { : "rejected", : "Rejected", : "red" },
] as ;

// Type-safe state
type  = <typeof , "value">;

function () {
  const [, ] = < | undefined>();

  // status is typed as "pending" | "approved" | "rejected" | undefined

  return (
    <
      ={}
      ={}
      ={}
      ="Filter by status"
    />
  );
}

Using with Grouped Options

Both utility types work seamlessly with grouped options:

import type { ,  } from "@/lib/typed-utils";

const  = [
  {
    : "Asia",
    : [
      { : "jp", : "Japan" },
      { : "kr", : "South Korea" },
      { : "vn", : "Vietnam" },
    ],
  },
  {
    : "Europe",
    : [
      { : "de", : "Germany" },
      { : "fr", : "France" },
      { : "uk", : "United Kingdom" },
    ],
  },
] as ;

// Extracts the individual option (not the group)
type Country = <typeof >;
type Country = {
    readonly value: "jp";
    readonly label: "Japan";
} | {
    readonly value: "kr";
    readonly label: "South Korea";
} | {
    readonly value: "vn";
    readonly label: "Vietnam";
} | {
    readonly value: "de";
    readonly label: "Germany";
} | {
    readonly value: "fr";
    readonly label: "France";
} | {
    readonly value: "uk";
    readonly label: "United Kingdom";
}
// Extracts just the value type CountryCode = <typeof , "value">;
type CountryCode = "jp" | "kr" | "vn" | "de" | "fr" | "uk"

API Reference

ExtractOption<TOptions>

Extracts the inner option type from an options array.

type ExtractOption<TOptions> = TOptions extends readonly (infer T)[]
  ? T extends { options: readonly (infer O)[] }
    ? O
    : T
  : never;
ParameterDescription
TOptionsThe options array type (should be readonly with as const)

Returns: Union type of all option objects in the array.

Behavior:

  • For flat options: Returns the option type directly
  • For grouped options: Extracts and returns the inner option type from options property

ExtractOptionValue<TOptions, TValueKey>

Extracts the value type from options based on the specified key.

type ExtractOptionValue<
  TOptions,
  TValueKey extends string,
> = ExtractOption<TOptions> extends infer O
  ? O extends Record<string, unknown>
    ? TValueKey extends keyof O
      ? O[TValueKey] & {}
      : never
    : never
  : never;
ParameterDescription
TOptionsThe options array type (should be readonly with as const)
TValueKeyThe key to extract values from (e.g., "value", "id")

Returns: Union type of all values for the specified key.

Tips

Always use as const

For proper type inference, always use as const when defining your options:

// Good - literal types preserved
const options = [
  { value: "a", label: "A" },
  { value: "b", label: "B" },
] as const;

// Bad - types widened to string
const options = [
  { value: "a", label: "A" },
  { value: "b", label: "B" },
];

Define options outside components

Define your options array outside the component to prevent recreation on each render and ensure stable type inference:

// Good - defined outside
const options = [...] as const;

function MyComponent() {
  return <TypedSelect options={options} />;
}

// Avoid - recreated on each render
function MyComponent() {
  const options = [...] as const; // recreated
  return <TypedSelect options={options} />;
}

On this page