NgRx Signal Store动态Key调用状态的TypeScript错误解决
修复NGRX Signal Store动态属性的TypeScript类型错误
问题描述
尝试定义可复用的Feature Store,希望状态包含动态定义的属性,代码实现如下:
import {computed} from '@angular/core'; import {signalStoreFeature, type, withComputed} from '@ngrx/signals'; export const withFeature = <Details>(config: { entityName: string }) => signalStoreFeature( {state: {[`${config.entityName}Details`]: type<Details>()}}, withComputed((store) => ({ [`${config.entityName}ComputedProp`]: computed(() => store[`${config.entityName}Details`]().detailsProp), // TS error here })), );
出现TypeScript错误:
TS7053: Element implicitly has an any type because expression of type
${string}Detailscan't be used to index type {}.
需要在保持动态定义属性的前提下修复该错误。
修复方案
严谨型方案(推荐)
核心是通过泛型约束和显式类型定义,让TypeScript能准确推断动态属性的类型:
import {computed} from '@angular/core'; import {signalStoreFeature, type, withComputed, type SignalStoreFeature} from '@ngrx/signals'; // 将entityName约束为字符串字面量类型,让TS跟踪具体属性名 export const withFeature = <Details, EntityName extends string>(config: { entityName: EntityName }) => { // 显式定义状态结构,明确动态属性的类型映射 type FeatureState = { [K in `${EntityName}Details`]: Details; }; return signalStoreFeature<FeatureState>( {state: {[`${config.entityName}Details`]: type<Details>()} as FeatureState}, withComputed((store: FeatureState) => ({ [`${config.entityName}ComputedProp`]: computed(() => store[`${config.entityName}Details` as keyof FeatureState].detailsProp ), })), ) as SignalStoreFeature<FeatureState, { [K in `${EntityName}ComputedProp`]: ReturnType<typeof computed<Details['detailsProp']>>; }>; };
关键优化点
- 新增
EntityName泛型并约束为字符串字面量,让TypeScript能识别具体的属性名称 - 定义
FeatureState类型,明确动态属性与Details类型的绑定关系 - 给
store参数指定FeatureState类型,消除索引类型歧义 - 断言返回的
SignalStoreFeature类型,确保计算属性的类型被正确推导
简洁型方案(快速修复)
如果不需要极致严谨的类型推导,可通过类型断言直接告诉TypeScript索引的类型:
import {computed} from '@angular/core'; import {signalStoreFeature, type, withComputed} from '@ngrx/signals'; export const withFeature = <Details>(config: { entityName: string }) => signalStoreFeature( {state: {[`${config.entityName}Details`]: type<Details>()}}, withComputed((store) => ({ [`${config.entityName}ComputedProp`]: computed(() => (store as Record<string, () => Details>)[`${config.entityName}Details`]().detailsProp ), })), );
内容的提问来源于stack exchange,提问作者Marius
相关产品推荐
相关产品推荐

