Skip to content

humanspeak/svelte-keyed

Repository files navigation

svelte-keyed

NPM version Build Status

License

Downloads CodeQL

TypeScript Types Maintenance

A writable derived store for objects and arrays with TypeScript support!

const user = writable({ name: { first: 'Rich', last: 'Harris' } })
const firstName = keyed(user, 'name.first')

$firstName = 'Bryan'

console.log($user) // { name: { first: 'Bryan', last: 'Harris' } };

Installation

npm i -D @humanspeak/svelte-keyed

Since Svelte automatically bundles all required dependencies, you only need to install this package as a dev dependency with the -D flag.

API

keyed takes a writable store and a keypath, and returns a writable store whose changes are reflected on the original store. The keypath can target nested properties in objects or elements in arrays.

Properties are accessed with dot notation, and arrays can be indexed with bracket notation:

const email = keyed(settings, 'profiles[0].email')
const firstItem = keyed(list, '[0]')

Nullable parents

If the parent store is nullable, then the child store will also be nullable:

type User = {
    name: {
        first: string
        last: string
    }
    relations: {
        partner?: User
    }
}

const maybeUser = writable<User | undefined>(undefined)
// Writable<string | undefined>
const firstName = keyed(maybeUser, 'name.first')

Nullable properties

Properties are accessed with optional chaining behavior:

const user = writable(initUser)
// Writable<Name | undefined>
const partnerName = keyed(user, 'relations.partner.name')

Array Operations

The store supports array operations through bracket notation:

const list = writable(['a', 'b', 'c'])
const firstItem = keyed(list, '[0]')
const lastItem = keyed(list, '[-1]') // Access last element

TypeScript

keyed provides full TypeScript support with type inference from the keypath:

const user = writable(initUser)
// Writable<string>
const firstName = keyed(user, 'name.first')

Type hints are provided for keypaths up to a depth of 3:

keyed(user, '...');
            ┌───────────────────────────────┐
              name                        
              name.first                  
              name.last                   
              relations                   
              relations.partner           
              relations.partner.name      
            └───────────────────────────────┘

Note: The depth limit is due to TypeScript's requirement that structured types be generated statically. While deeper paths will work, they won't show in autocomplete.

Use Cases

Context Stores

Perfect for setting store properties in component context:

<!-- Settings.svelte -->
<script>
    setContext('profileSettings', keyed(settings, 'profile'))
</script>

<GeneralSettings />
<ProfileSettings />

Action Parameters

Ideal for passing store segments to Svelte actions:

<!-- Settings.svelte -->
<script>
    const stats = writable({ userClicks: 0, userTaps: 0 })
    const clicks = keyed(stats, 'userClicks')
</script>

<div use:trackClicks={clicks} />

Store Composition

Combine with other store operations for complex state management:

const settings = writable({ theme: 'light', fontSize: 16 })
const theme = keyed(settings, 'theme')
const isDarkMode = derived(theme, $theme => $theme === 'dark')