-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: improve TypeScript definitions (#1)
- Loading branch information
Showing
5 changed files
with
198 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,61 @@ | ||
export default function svelteFsm(state: string | symbol, states: object): function; | ||
type BaseState = string | symbol; | ||
|
||
type BaseAction = string; | ||
|
||
type BaseStates<State extends BaseState = BaseState> = Record<State, BaseActions>; | ||
|
||
type Args = any[]; | ||
|
||
type LifecycleAction = (arg: { | ||
from: BaseState | null; | ||
to: BaseState; | ||
event: BaseAction | null; | ||
args: Args; | ||
}) => void; | ||
|
||
type ActionFunction = BaseState | ((...args: Args) => BaseState) | ((...args: Args) => void); | ||
|
||
type BaseActions = { | ||
_enter?: LifecycleAction; | ||
_exit?: LifecycleAction; | ||
[key: BaseAction]: ActionFunction; | ||
}; | ||
|
||
type DetectFallBackState<State extends BaseState> = State extends '*' ? string : State; | ||
|
||
type ExtractStates<States extends BaseStates> = DetectFallBackState<Exclude<keyof States, number>>; | ||
|
||
type ExtractObjectValues<Object> = Object[keyof Object]; | ||
|
||
type GetActionFunctionMapping<Actions extends BaseActions> = { | ||
[Key in Exclude<keyof Actions, '_enter' | '_exit'>]: Actions[Key] extends BaseState | ||
? () => Actions[Key] | ||
: Actions[Key]; | ||
}; | ||
|
||
type GetActionMapping<States extends BaseStates> = ExtractObjectValues<{ | ||
[Key in keyof States]: GetActionFunctionMapping<States[Key]>; | ||
}>; | ||
|
||
type ExtractActions<States extends BaseStates> = GetActionMapping<States>; | ||
|
||
type Unsubscribe = () => void; | ||
|
||
type Subscribe<S extends BaseState> = (callback: (state: S) => void) => Unsubscribe; | ||
|
||
type StateMachine<State extends BaseState, Actions> = { | ||
[Key in keyof Actions]: Actions[Key]; | ||
} & { | ||
subscribe: Subscribe<State>; | ||
}; | ||
|
||
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void | ||
? I | ||
: never; | ||
|
||
declare const svelteFsm: <Sts extends Readonly<BaseStates>, S extends ExtractStates<Sts>>( | ||
state: S, | ||
states: Sts | ||
) => StateMachine<ExtractStates<Sts>, UnionToIntersection<ExtractActions<Sts>>>; | ||
|
||
export default svelteFsm; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* Verify type declarations found in index.d.ts by running: | ||
* $ npx tsc --noEmit --target es6 test/types.ts | ||
*/ | ||
import fsm from '../index.js'; | ||
|
||
// @ts-expect-error fsm expects 2 arguments (0 provided) | ||
const invalid1 = fsm(); | ||
// @ts-expect-error fsm expects 2 arguments (1 provided) | ||
const invalid2 = fsm('foo'); | ||
// @ts-expect-error fsm expects string or symbol for initial state (null provided) | ||
const invalid3 = fsm(null, {}); | ||
// @ts-expect-error fsm expects string or symbol for initial state (number provided) | ||
const invalid4 = fsm(1, {}); | ||
// @ts-expect-error fsm expects object for states (string provided) | ||
const invalid5 = fsm('foo', 'bar'); | ||
// @ts-expect-error fsm expects initial state to match a defined state or fallback | ||
const invalid6 = fsm('foo', {}); | ||
|
||
const invalid7 = fsm('foo', { | ||
foo: { | ||
// @ts-expect-error state expects action to be string or function (object provided) | ||
bar: {}, | ||
// @ts-expect-error state expects action to be string or function (number provided) | ||
baz: 1, | ||
// @ts-expect-error state expects lifecycle action to be function (string provided) | ||
_enter: 'bar' | ||
} | ||
}); | ||
|
||
// A simple, valid state machine | ||
const valid1 = fsm('off', { | ||
off: { | ||
toggle: 'on' | ||
}, | ||
on: { | ||
toggle() { | ||
return 'off'; | ||
} | ||
} | ||
}); | ||
|
||
// @ts-expect-error subscribe expects callback | ||
valid1.subscribe(); | ||
const unsub = valid1.subscribe(() => {}); | ||
// @ts-expect-error unsubscribe expects no arguments | ||
unsub('foo'); | ||
unsub(); | ||
|
||
// @ts-expect-error state machine expects valid event invocation | ||
valid1.noSuchAction(); | ||
|
||
// @ts-expect-error toggle expects no arguments (1 provided) | ||
valid1.toggle(1); | ||
valid1.toggle(); | ||
|
||
const toggleResultValid: string | symbol = valid1.toggle(); | ||
// @ts-expect-error toggle returns string or symbol | ||
const toggleResultInvalid: number = valid1.toggle(); | ||
|
||
// A state machine with fallback state (any initial state permitted) | ||
const valid2 = fsm('initial', { | ||
'*': { | ||
foo: () => {} | ||
} | ||
}); | ||
valid2.foo(); | ||
|
||
// A state machine with overloaded action signatures | ||
const valid3 = fsm('initial', { | ||
'*': { | ||
overloaded(one: number) { | ||
return 'foo'; | ||
} | ||
}, | ||
foo: { | ||
overloaded(one: string, two: number) {} | ||
} | ||
}); | ||
// @ts-expect-error overloaded expects 1 or 2 args (0 provided) | ||
valid3.overloaded(); | ||
// @ts-expect-error overloaded expects first argument as number | ||
valid3.overloaded('string'); | ||
valid3.overloaded(1); | ||
// @ts-expect-error overloaded expects first argument as string | ||
valid3.overloaded(1, 2); | ||
valid3.overloaded('string', 2); | ||
// @ts-expect-error overloaded expects 1 or 2 args (3 provided) | ||
valid3.overloaded(1, 2, 3); | ||
|
||
// @ts-expect-error overloaded with single argument returns string | void | ||
const overloadedResult1Invalid: void = valid3.overloaded(1); | ||
const overloadedResult1Valid: string | void = valid3.overloaded(1); | ||
|
||
// @ts-expect-error overloaded with two arguments returns only void | ||
const overloadedResult2Invalid: string = valid3.overloaded('string', 1); | ||
const overloadedResult2Valid: string | void = valid3.overloaded('string', 1); | ||
|
||
// A state machine that uses symbols as a state keys | ||
const valid4 = fsm(Symbol.for('foo'), { | ||
[Symbol.for('foo')]: { | ||
bar: Symbol.for('bar') | ||
} | ||
}); | ||
const symbolResultValid: string | symbol = valid4.bar(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters