-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: improve TypeScript definitions #1
Conversation
@kenkunz do you have the intention to convert all files to TypeScript? |
@ivanhofer thanks for the PR! I'm focused on completing a feature and then taking a break for the U.S. Thanksgiving weekend – will take a closer look at this early next week. Vielen Dank! |
@ivanhofer I see you've continued to improve the TS declarations 👍 Overall comment: this looks great! I can see how this will improve the dev experience for TS developers and for anyone relying on IntelliSense / IDE feedback. Thanks for your time and effort on this! I've checked out the PR and started testing the declarations. I will add separate comments to address specific questions or feedback. |
@ivanhofer replying to your earlier question:
Not at this time. I don't have much experience with TypeScript – I've dabbled with it just enough to get a feel for it, and find that I prefer vanilla JavaScript. (My favorite language is Ruby – a very dynamic language that intentionally separates the notion of class from type ("duck typing" – if it quacks like a duck, it is one).
I tend to lean heavily on unit tests and dynamic type checking (and only if/where it's really needed). The current surface area of the library is pretty small (and I hope to keep it that way), so I think the risk of using an inappropriate type internally is minimal. All of that said… I can definitely see the benefits of exposing type declarations for users of the library! |
1. Basic use case feedbackAll the basic cases below make sense… with the exception of the initial state validation (will address in a separate comment). Let me know if this style of feedback works for you – I'm basically writing test cases with pass/fail (✔︎/✘) comments const machine = fsm('baz', { // ✘ leaning against validating initial state
'*': {
abc: () => {}
},
foo: {
def: 'bar'
},
bar: {}
});
machine.subscribe(); // ✔︎ warning expected
const unsub = machine.subscribe(() => {}); // ✔︎ no warning expected
unsub.foo(); // ✔︎ warning expected
unsub('foo'); // ✔︎ warning expected
unsub(); // ✔︎ no warning expected
machine.abc(); // ✔︎ no warning expected
machine.def(); // ✔︎ no warning expected
machine.ghi(); // ✔︎ warning expected (no matching action) |
2. Initial state
|
3.
|
@ivanhofer I have a couple other areas for feedback / clarification, but I'll pause for now and add some additional comments tomorrow. Let me know if you have any questions or feedback on the items above. Thanks! |
@kenkunz thanks for your detailed responses. typescript files
I personally find it easier to write the type definitions directly in the source code. It offers a instant feedback loop, because you see when an implemetation collides with the types. strict modeI haven't thought about the usecase where someone could leave out the "final" state. fallback stateIs the
|
There still is a TODO from my side: |
typescript filesYou make some very good points. Will consider making this change when implementing a new feature or change to the API. |
strict modeI agree this would probably increase maintenance. Hopefully it could be modularized to minimize overhead. |
fallback stateI added this in
That's a great suggestion if it's not overly complicated. I can think of no reason you'd want to initialize an FSM in a state that has no actions. |
testsI didn't know about the |
optional actions
That's correct. I rely on this pattern extensively in UI code – attach an FSM-based hander Essentially, this means the default behavior for any action is a no-op. We could choose to make this explicit – you'd need to add a no-op to the fallback state, or maybe support This feature – combined with the fact that the resulting state is returned from event invocations – yields an interesting side-effect. Assuming This is more of an accident than an intended feature (I don't think I'm relying on it anywhere). I don't think the type-checking needs to allow invocations for actions that are not defined on any state (or fallback). If someone really wants to leverage this and not get a warning or type error, they can add a |
LifecycleAction typeI had a question about the LifecycleAction type declaration From the docs:
So the following const machine = fsm('foo', {
foo: {
_enter: console.log
}
}); …immediately produces Is anything special needed in the type declaration to allow |
LifecycleAction typeI now have added the LifecycleAction typeThe testsHow should we proceed? Do you want me to setup some basic |
👍
I think it's a good idea – I'd like to avoid errors/warnings for intentionally supported use cases.
You've done a ton of work already – why don't I take the first stab at this. I'll let you know if I have any questions. Thanks! |
I pushed my first pass at type declaration tests – see 076b228
Let me know if you have any feedback on this overall structure, as well as feedback on the type tests themselves. Thanks! |
will take a look at your changes by the end of this week 👍 |
Hi @kenkunz, Your tests look good. I have added a few tests that detect if a wrong type of argument was passed to an action. |
One minor suggestion: we could add a GH Action to run tests on every push to master. So you would get notified if you forgot tu run the tests before pushing. |
Hi @ivanhofer, Regarding optional actions / Nice additions to the type tests 👍. I agree – the PR is looking good! I'll address the one item mentioned above and make some small formatting changes (spaces v. tabs and semicolons). I'm not super opinionated about these things other than valuing consistency. I haven't added I should be merging this within the next day or so. I have a few other minor changes to incorporate alongside this (minor bug fix and README updates), after which I will publish Thank you again for all your work on this! -Ken |
@ivanhofer – regarding:
… if you have a moment, take a quick look at 6c3a801 to see if this looks correct (sorry… TypeScript n00b!). Type tests are passing… so I'm cautiously optimistic I didn't screw this up 🤞. |
Ah sorry, I was confused by the @kenkunz your last commit looks good. Happy to help! Just let me know if you need help in the future ;) |
I have added a few TypeDefinitions.
This PR is still WIP.
Implemented typesafety/autocompletion features:
typesafe initial state
typed subscribe function
typed action calls
halfly-typed arguments for lifecycle methods
(the arguments are typed as string, but aren't restricted to the exact state values, like
'initial' | 'final'
)only allow actions that the state-machine can handle
Please let me know if I made wrong assumptions about the functionality of the state machine code.