Getting a single action type (not using `union`)
Closed this issue · 8 comments
Hi.
I've been using InstanceType<typeof ATestAction>
for single action types, but is there a less verbose way to do this?
The reason for this is we sometimes return an array of one type of action from an epic and use this in tests:
const arrayOfTest: InstanceType<typeof ATestAction>[] = [
{
type: 'A_TEST_ACTION',
},
]
Sure, it's certainly no deal breaker, especially as we don't use this often, but it'd be great to know if there's a shorter method if possible.
I tried making a getType (which is probably the wrong name) but it ended-up being even more wordy (and made InstanceType more attractive):
export function getType<
C extends ActionCtor<string, {}, Ctor<{}>>
>(ctors: C): C['action'] {
return undefined!
}
const Action = getType(ASecondTestAction)
type TAction = typeof Action
const test: TAction[] = [
{
type: 'A_TEST_ACTION',
},
]
console.log(test)
There might be. IIRC, sometime after writing the library, I discovered that it's possible to get the same behaviour as a class
by declaring a constructor and a type - an interface
, I think - with the same name.
I'll have to check my notes. Doing that might do away with the need for union
altogether and would solve your problem with the verbosity, too.
I'll let you know how it goes.
Yes, this is how we're currently writing actions:
export class ATestAction {
public static readonly TYPE = 'TEST/ACTION'
public static create(thing: string): ATestAction {
return {
type: ATestAction.TYPE,
thing,
}
}
public readonly type = ATestAction.TYPE
public readonly thing: string
}
Hence why I've been attracted to this lib, if I can simply write:
const ATestAction = action('TEST/ACTION', props<{ thing: string }>())
The only downside I've found to this is the slightly longer single interface notation.
The concept I mentioned is this:
interface Thing { name: string; }
interface ThingCtor { new (name: string): Thing; }
const Thing: ThingCtor = function (this: Thing, name: string): void { this.name = name; } as any;
Here, Thing
is both a constructor and a type. However, on reflection, I don't see how I can use this in ts-action
. I don't see how I can declare the type.
You could do this, though:
const Test = action('TEST/ACTION', props<{ thing: string }>());
type Test = typeof Test.action;
And, thereafter, you can use Test
as both an action constructor and a type.
Thanks! That certainly takes our eight lines down to two. Would be great to get it down to a one liner. Would this guy's repo help? He uses class extensions.
TBH, the classes are something that I intend to remove, so being forced into relying upon the use of a class
isn't especially appealing.
I have, in my own issue tracker, a task to provide an alternate implementation that uses simple creator functions instead of classes. The current, class
-based implementation would be deprecated and later discarded.
I use React most, these days, and having to use class
instances and the icky prototype hack is something I'd like to not have to do.
Okay, sounds good. I look forward to seeing the next version. Thanks for your help.
Anyway, if creating the array is your primary concern, you could write an array
function (or whatever) to do something like this:
function array<C extends ActionCtor<string, {}, Ctor<{}>>>(...actions: C["action"]): C["action"][] { return actions; }
const fooActions = array<Foo>({ /*...*/ }, { /*...*/ }, { /*...*/ });
I think now that each action has its type defined, I'd just use ATestAction[]
.