Getting correct types for stores using custom features with "generic" field-names #4538
-
I've been playing around with writing custom signalstore features, heavily referencing this article from Manfred Steyer about custom extensions. By "generic" field-names I main field-names with a user-defined prefix or suffix to avoid name-collisions, such as a user defining a "name" parameter of a feature and based on that you generate fields such as What I've achieved is a custom extension that works, but the type of the feature doesn't appear to impact the SignalStore it gets used in. I have this example and the only info I'm getting from autocompletion is that // withExample.ts - SignalStoreFeature with types that should impact the ExampleStore type
import {
signalStoreFeature,
SignalStoreFeature,
withState,
} from '@ngrx/signals';
export type ExampleState<Prop extends string> = {
[K in Prop as `${K}Field1`]: string;
};
export type EmptyFeature = {
state: {};
signals: {};
methods: {};
computed: {};
};
export type FilledFeature<Prop extends string> = {
state: ExampleState<Prop>;
computed: {};
methods: {};
};
export function withExample<Prop extends string>(
name: Prop,
): SignalStoreFeature<EmptyFeature, FilledFeature<Prop>> {
return signalStoreFeature(
withState<ExampleState<Prop>>({
[`${name}Field1`]: 'foo',
} as ExampleState<Prop>),
);
} // exampleStore.ts - ExampleStore as a type should have the field `exampleField1` in its type from `withExample`
import { signalStore, withMethods } from '@ngrx/signals';
import { withExample } from 'src/utils/signalStoreFeatures/requestFeatures/withExample';
export const ExampleStore = signalStore(
{ providedIn: 'root' },
withExample<string>('example'),
); // exampleService.ts - Just there to experiment with autosuggestions
import { inject, Injectable } from '@angular/core';
import { ExampleStore } from 'src/app/example/exampleStore';
@Injectable({
providedIn: 'root'
})
export class ExampleService{
constructor() {
const store = inject(ExampleStore);
// You should be able to see `exampleField1` pop up here as suggestion for `store.ex`
}
} Adding a second feature declaration function to export function withExample<Prop extends string>(
name: Prop,
): SignalStoreFeature<EmptyFeature, FilledFeature<Prop>>; Does not fix this issue. Is there any other mistake I'm making? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
I'm not even half a smart as I wish I were. My problem is that the generic param I provide (Prop) is What I should be writing is this: That means the actual field generated from the value-parameter Now what I wonder more is: Is there a way for me to be able to just write:
I would prefer explicitly to write with 'example' as a generic type because you might have a feature with N generic types and in such cases you must write the generic parameter 'example' out. |
Beta Was this translation helpful? Give feedback.
I'm not even half a smart as I wish I were. My problem is that the generic param I provide (Prop) is
string
, not the value I want.What I should be writing is this:
withExample<'example'>('example')
What I did write was this:
withExample<string>('example')
That means the actual field generated from the value-parameter
"example"
inname
were generated properly. However in my second example thefield-type
generated from the generic parameter 'example' inProp
naturally were not - how would they, string doesn't tell you anything.Now what I wonder more is: Is there a way for me to be able to just write:
withExample<'example'>()
instead ofwithExample<'example'>('example')
?I would prefer ex…