From bf9d7d4c51b243af32a064f3a50f48ab1a359341 Mon Sep 17 00:00:00 2001 From: Devansh Jethmalani Date: Mon, 20 Sep 2021 00:06:14 +0530 Subject: [PATCH] feat(dx): instantiate all boundary types (#76) --- src/index.ts | 2 +- src/types.ts | 103 +++++++++++++++++------------------- test/index.test.ts | 2 +- test/types.twoslash-test.ts | 37 ++++++++++++- 4 files changed, 87 insertions(+), 57 deletions(-) diff --git a/src/index.ts b/src/index.ts index 7ab82e3..a82f081 100644 --- a/src/index.ts +++ b/src/index.ts @@ -144,4 +144,4 @@ const createLogger = (definition: Machine.Definition.Impl) => (groupLabel: strin const useStateMachine = useStateMachineImpl as unknown as UseStateMachine; export default useStateMachine; -export const t = () => ({ [$$t]: undefined as T }) +export const t = () => ({ [$$t]: undefined as unknown as T }) diff --git a/src/types.ts b/src/types.ts index 5fb5090..f05cbe8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,14 +3,13 @@ import { R } from "./extras" export type UseStateMachine = >(definition: A.InferNarrowestObject) => - [ state: Machine.State> - , send: Machine.Send> + [ state: A.Instantiated>> + , send: A.Instantiated>> ] export const $$t = Symbol("$$t"); type $$t = typeof $$t; -export type CreateType = - () => { [$$t]: T } +export type CreateType = () => { [$$t]: T } export namespace Machine { export type Definition< @@ -128,9 +127,10 @@ export namespace Machine { | { target: TargetString , guard?: ( parameter: - { context: Machine.Context - , event: U.Extract, Event> - } + A.Instantiated< + { context: A.Uninstantiated> + , event: A.Uninstantiated, Event>> + }> ) => boolean } @@ -150,9 +150,9 @@ export namespace Machine { export type Effect>> = - (parameter: EffectParameterForStateValue) => + (parameter: A.Instantiated>) => | void - | ((parameter: EffectCleanupParameterForStateValue) => void) + | ((parameter: A.Instantiated>) => void) type EffectImpl = (parameter: EffectParameter.Impl) => @@ -239,8 +239,8 @@ export namespace Machine { export type Event> = | O.Value<{ [T in U.Exclude]: - A.Get extends infer E - ? E extends any ? O.ShallowClean<{ type: T } & E> : never + A.Get extends infer P + ? P extends any ? O.ShallowMerge<{ type: T } & P> : never : never }> | ( A.Get extends true ? never : @@ -271,26 +271,10 @@ export namespace Machine { } export namespace EffectParameter { - export interface EffectParameterForStateValue - extends Base - { event: Machine.EntryEventForStateValue - } - export namespace Cleanup { - export interface ForStateValue - extends Base - { event: Machine.ExitEventForStateValue - } - export type Impl = EffectParameter.Impl } - export interface Base - { send: Machine.Send - , context: Machine.Context - , setContext: Machine.SetContext - } - export type Impl = EffectParameterImpl; } export interface EffectParameterImpl @@ -302,17 +286,17 @@ export namespace Machine { export interface EffectParameterForStateValue extends BaseEffectParameter - { event: Machine.EntryEventForStateValue + { event: A.Uninstantiated> } export interface EffectCleanupParameterForStateValue extends BaseEffectParameter - { event: Machine.ExitEventForStateValue + { event: A.Uninstantiated> } export interface BaseEffectParameter { send: Machine.Send - , context: Machine.Context + , context: A.Uninstantiated> , setContext: Machine.SetContext } @@ -368,8 +352,8 @@ export namespace Machine { } export type Send = - { (sendable: U.Exclude, A.String>): void - , (sendable: U.Extract, A.String>): void + { (sendable: A.Uninstantiated, A.String>>): void + , (sendable: A.Uninstantiated, A.String>>): void } type SendImpl = (send: Sendable.Impl) => void @@ -386,7 +370,9 @@ export namespace Machine { export type Impl = SetContextImpl; } - export type ContextUpdater = (context: Context) => Context + export type ContextUpdater = + (context: A.Uninstantiated>) => + A.Uninstantiated> type ContextUpdaterImpl = (context: Context.Impl) => Context.Impl export namespace ContextUpdater { @@ -403,8 +389,8 @@ export namespace Machine { > = Value extends any ? { value: Value - , context: Context - , event: EntryEventForStateValue + , context: A.Uninstantiated> + , event: A.Uninstantiated> , nextEventsT: A.Get, "type">[] , nextEvents: NextEvents } @@ -446,11 +432,6 @@ export namespace S { : false; } -export namespace F { - export type Call = F extends (...args: any[]) => infer R ? R : never; - export type Parameters = F extends (...args: infer A) => any ? A : never; -} - export namespace U { export type Extract = T extends U ? T : never; export type Exclude = T extends U ? never : T; @@ -458,12 +439,11 @@ export namespace U { export namespace O { export type Value = T[keyof T]; - export type ShallowClean = { [K in keyof T]: T[K] } + export type ShallowMerge = { [K in keyof T]: T[K] } & unknown } export namespace A { export type Cast = T extends U ? T : U; - export type Fallback = T extends U ? T : U; export type Tuple = T[] | [T]; export type Object = object; export type String = string; @@ -508,10 +488,6 @@ export namespace A { P extends [infer K1, ...infer Kr] ? K1 extends keyof T ? _Get : - K1 extends Get.Returned$$ ? - _Get infer R ? R : undefined, Kr, F> : - K1 extends Get.Parameters$$ ? - _Get any ? A : undefined, Kr, F> : F : never @@ -520,14 +496,6 @@ export namespace A { ? A.Cast : never - export namespace Get { - const Returned$$ = Symbol("Returned$$"); - export type Returned$$ = typeof Returned$$; - - const Parameters$$ = Symbol("Parameters$$"); - export type Parameters$$ = typeof Parameters$$; - } - export type CustomError = Place extends (S.IsLiteral extends true ? Error : A.String) ? Place extends `${S.Assert} ` @@ -535,6 +503,33 @@ export namespace A { : `${S.Assert} ` : Error + export type Instantiated = + T extends Uninstantiated ? U : + T extends Builtin ? T : + T extends any + ? T extends A.Function + ? T extends { (...a: infer A1): infer R1, (...a: infer A2): infer R2 } + ? { (...a: Instantiated): Instantiated + , (...a: Instantiated): Instantiated + } : + T extends (...a: infer A1) => infer R1 + ? (...a1: Instantiated) => Instantiated : + never : + T extends A.Object + ? { [K in keyof T]: Instantiated } : + T + : never + + type Builtin = + | { [Symbol.toStringTag]: string } + | Error + | Date + | RegExp + | Generator + + export type Uninstantiated = T & { [$$uninstantiated]: true } + declare const $$uninstantiated: unique symbol; + export type Tag = { [_ in N]: void } diff --git a/test/index.test.ts b/test/index.test.ts index 570dbcf..f3defc6 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -13,7 +13,7 @@ const clearLog = () => const useStateMachine = ((d: any) => _useStateMachine({ ...d, console: { log: logger } }) - ) as typeof _useStateMachine + ) as any as typeof _useStateMachine describe("useStateMachine", () => { describe("States & Transitions", () => { diff --git a/test/types.twoslash-test.ts b/test/types.twoslash-test.ts index f45be5e..72bca5d 100644 --- a/test/types.twoslash-test.ts +++ b/test/types.twoslash-test.ts @@ -2,7 +2,7 @@ import { A, LS, UseStateMachine, CreateType } from "../src/types"; const useStateMachine = (() => []) as any as UseStateMachine; -const t = (() => {}) as CreateType +const t = (() => undefined) as unknown as CreateType const query = () => ((global as any).twoSlashQueries.shift()) as { completions: string[], text: string } @@ -1273,3 +1273,38 @@ describe("workaround for #65", () => { } >()) }) + +describe("A.Instantiated", () => { + it("does not instantiate builtin objects", () => { + let _x: A.Instantiated = new Date() + _x; +// ^? + expect(query().text).toContain("Date") + }) + + it("does not instantiate context", () => { + interface Something { foo: string } + let [_state] = useStateMachine({ + // ^? + context: { foo: "" } as Something, + initial: "a", + states: { a: {} } + }) + + expect(query().text).toContain("Something") + }) + + it("does not instantiate event payloads deeply", () => { + interface Something { foo: string } + let [_, _send] = useStateMachine({ + // ^? + schema: { + events: { A: t<{ bar: Something }>() } + }, + initial: "a", + states: { a: { on: { A: "a" } } } + }) + + expect(query().text).toContain("Something") + }) +})