You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Svelte Writable Store的Getter/Setter设置不同TypeScript类型?

解决SvelteKit中Writable Store的类型差异问题(Setter无ID,Getter带ID)

首先明确两个核心类型:用户传入的无ID对象,以及Store内部存储/返回的带ID对象:

// 用户传入的无ID对象类型
type ItemWithoutId = {
  type: "MyType";
  properties: {
    foo: string;
  };
};

// Store内部维护的带ID对象类型
type ItemWithId = ItemWithoutId & { id: string };

原生writable的set/update与subscribe的类型绑定一致,无法直接实现「输入无ID、输出有ID」的类型差异,因此需要封装自定义Store,在内部处理ID生成逻辑,同时对外暴露符合要求的类型接口:

import { writable, type Writable } from 'svelte/store';

// 实现唯一ID生成逻辑(可替换为uuid等库)
function generateUniqueId(): string {
  return Math.random().toString(36).slice(2, 10);
}

// 自定义Store工厂函数
export function createItemStore(initialItems: ItemWithoutId[] = []) {
  // 初始化时为初始数据自动添加ID
  const initialWithIds: ItemWithId[] = initialItems.map(item => ({
    ...item,
    id: generateUniqueId()
  }));

  // 内部使用原生Writable维护带ID的数据
  const internalStore: Writable<ItemWithId[]> = writable(initialWithIds);

  // 自定义set方法:仅接受无ID数组,自动添加ID后更新内部Store
  function set(items: ItemWithoutId[]) {
    const itemsWithIds = items.map(item => ({
      ...item,
      id: generateUniqueId()
    }));
    internalStore.set(itemsWithIds);
  }

  // 自定义update方法:支持基于现有带ID数据更新,新增项自动补全ID
  function update(
    updater: (current: ItemWithId[]) => (ItemWithoutId | ItemWithId)[]
  ) {
    internalStore.update(current => {
      return updater(current).map(item => {
        // 已有ID的项直接保留,无ID的项自动生成ID
        return 'id' in item ? item as ItemWithId : { ...item, id: generateUniqueId() };
      });
    });
  }

  // 对外暴露subscribe(返回带ID数据)和自定义的set/update(接受无ID输入)
  return {
    subscribe: internalStore.subscribe,
    set,
    update
  };
}

使用示例

// 创建Store,初始传入无ID数组
const appStore = createItemStore([
  { type: "MyType", properties: { foo: "bar" } }
]);

// ✅ 调用set时,TypeScript会强制检查输入必须无ID
appStore.set([
  { type: "MyType", properties: { foo: "baz" } }
]);

// ✅ 调用update时,可直接访问当前带ID的数据,新增项允许无ID
appStore.update(current => {
  console.log(current[0].id); // 这里能获得正确的IntelliSense提示
  return [...current, { type: "MyType", properties: { foo: "new-item" } }];
});

// ✅ 订阅时拿到的是带ID的数组,类型提示完全正确
appStore.subscribe(items => {
  items.forEach(item => {
    console.log(item.id); // 自动提示id字段
    console.log(item.type, item.properties.foo);
  });
});

关键说明

  1. 避免了原代码中subscribe内调用set导致的无限循环问题,ID生成逻辑统一放在set和update阶段处理。
  2. 通过封装实现了「输入输出类型分离」:对外的set/update仅接受无ID数据,subscribe返回带ID数据,完全符合需求。
  3. 全程未使用联合类型,TypeScript类型检查和IntelliSense提示均正常工作。

内容的提问来源于stack exchange,提问作者Laaouatni Anas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 20:28:14