From bbe8bbd387563ab3045b5aedff196b86fe2cc01f Mon Sep 17 00:00:00 2001 From: Teun Mooij <tgmooij@hotmail.com> Date: Sun, 23 Jun 2024 22:18:46 +0200 Subject: [PATCH] Dependency feedback (#13) * improve error messages * improve missing dependency messages * fix error validation type distribution --- src/types/dependencies.ts | 6 +++--- src/types/systemic.ts | 30 ++++++++++++++++-------------- test/types/dependencies.spec.ts | 5 +++-- test/types/systemic.spec.ts | 24 +++++++++++++++--------- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/types/dependencies.ts b/src/types/dependencies.ts index c32be94..2a39185 100644 --- a/src/types/dependencies.ts +++ b/src/types/dependencies.ts @@ -71,9 +71,9 @@ type ValidateMappingDependency< ? [DependencyValidationError<string, unknown, unknown>] // Dependency not created as constant : [PropAt<TDependencies, DependencyDestinationOf<TMapping>>] extends [never] ? [] // Unexpected dependency - : Injected<TSystemic, TCurrent, TMapping> | undefined extends - | PropAt<TDependencies, DependencyDestinationOf<TMapping>> - | undefined + : [Injected<TSystemic, TCurrent, TMapping>] extends [ + PropAt<TDependencies, DependencyDestinationOf<TMapping>>, + ] ? [] // Correct dependency : [ DependencyValidationError< diff --git a/src/types/systemic.ts b/src/types/systemic.ts index 561b4b5..31ea449 100644 --- a/src/types/systemic.ts +++ b/src/types/systemic.ts @@ -150,7 +150,7 @@ export type DependsOn< infer First extends DependencyValidationError<any, any, any>, ...any[], ] - ? SystemicWithInvalidDependency<First> + ? SystemicWithInvalidDependency<TCurrent, First> : SystemicBuild< TSystemic, TCurrent, @@ -158,23 +158,25 @@ export type DependsOn< >; }; -export type IncompleteSystemic<TMissing> = { +export type IncompleteSystemic<TCurrent extends string, TMissing> = { [X in keyof Systemic<any>]: ( - error: `Please add missing dependencies`, + error: `Please add missing dependencies for component "${TCurrent}"`, expected: StripEmptyObjectsRecursively<DeepRequiredOnly<TMissing>>, ) => void; }; -export type SystemicWithInvalidDependency<TError extends DependencyValidationError<any, any, any>> = - { - [X in keyof Systemic<any>]: ( - error: string extends TError[0] - ? "Destination of a dependency is unknown. Did you neglect to mark it 'as const'?" - : `Dependency "${TError[0]}" is not of the required type`, - expected: TError[1], - actual: TError[2], - ) => void; - }; +export type SystemicWithInvalidDependency< + TCurrent extends string, + TError extends DependencyValidationError<any, any, any>, +> = { + [X in keyof Systemic<any>]: ( + error: string extends TError[0] + ? `Destination of a dependency for component "${TCurrent}" is unknown. Did you neglect to mark it 'as const'?` + : `Dependency "${TError[0]}" on component "${TCurrent}" is not of the required type`, + expected: TError[1], + actual: TError[2], + ) => void; +}; export type SystemicBuild< TSystemic extends Record<string, Registration<unknown, boolean>>, @@ -182,7 +184,7 @@ export type SystemicBuild< TDependencies extends Record<string, unknown>, > = [RequiredKeys<TDependencies>] extends [never] ? Systemic<TSystemic> & DependsOn<TSystemic, TCurrent, TDependencies> - : DependsOn<TSystemic, TCurrent, TDependencies> & IncompleteSystemic<TDependencies>; + : DependsOn<TSystemic, TCurrent, TDependencies> & IncompleteSystemic<TCurrent, TDependencies>; type SystemicBuildDefaultComponent< TSystemic extends Record<string, Registration<unknown, boolean>>, diff --git a/test/types/dependencies.spec.ts b/test/types/dependencies.spec.ts index aa0e019..c627708 100644 --- a/test/types/dependencies.spec.ts +++ b/test/types/dependencies.spec.ts @@ -93,11 +93,12 @@ describe("dependencies types", () => { it("validates an invalid simple dependency", () => { type Systemic = { foo: { component: { foo: string }; scoped: false }; + bar: { component: any; scoped: false }; }; - type Dependencies = { foo: { bar: number } }; + type Dependencies = { foo: { bar: number }; bar: string }; - type Given = ["foo"]; + type Given = ["foo", "bar"]; type Result = ValidateDependencies<Systemic, "foo", Dependencies, Given>; diff --git a/test/types/systemic.spec.ts b/test/types/systemic.spec.ts index 353cedd..bbe36fa 100644 --- a/test/types/systemic.spec.ts +++ b/test/types/systemic.spec.ts @@ -125,7 +125,7 @@ describe("systemic types", () => { foo: { component: { foo: string }; scoped: false }; bar: { component: number; scoped: false }; }; - type Expected = IncompleteSystemic<{ foo: { foo: string } }> & + type Expected = IncompleteSystemic<"bar", { foo: { foo: string } }> & DependsOn<Registrations, "bar", { foo: { foo: string } }>; expectTypes<typeof system, Expected>().toBeEqual(); @@ -144,7 +144,7 @@ describe("systemic types", () => { foo: { component: { foo: string; bar: { baz: string } }; scoped: true }; bar: { component: number; scoped: false }; }; - type Expected = IncompleteSystemic<{ foo: { baz: string } }> & + type Expected = IncompleteSystemic<"bar", { foo: { baz: string } }> & DependsOn<Registrations, "bar", { foo: { baz: string } }>; expectTypes<typeof system, Expected>().toBeEqual(); @@ -156,13 +156,13 @@ describe("systemic types", () => { .add("bar", { start: async (deps: { foo: { foo: number } }) => 42 }) .dependsOn("foo"); - type Expected = SystemicWithInvalidDependency<["foo", { foo: number }, { foo: string }]>; + type Expected = SystemicWithInvalidDependency<"bar", ["foo", { foo: number }, { foo: string }]>; expectTypes<typeof system, Expected>().toBeEqual(); expectTypes< (typeof system)["start"], ( - error: 'Dependency "foo" is not of the required type', + error: 'Dependency "foo" on component "bar" is not of the required type', expected: { foo: number }, actual: { foo: string }, ) => void @@ -215,7 +215,10 @@ describe("systemic types", () => { baz: { component: { foo: { bar: { qux: number } } }; scoped: true }; "foo.bar": { component: number; scoped: false }; }; - type Expected = SystemicWithInvalidDependency<["baz", { qux: string }, { qux: number }]>; + type Expected = SystemicWithInvalidDependency< + "foo.bar", + ["baz", { qux: string }, { qux: number }] + >; expectTypes<typeof system, Expected>().toBeEqual(); }); @@ -247,7 +250,7 @@ describe("systemic types", () => { "foo.bar": { component: { baz: number }; scoped: false }; qux: { component: number; scoped: false }; }; - type Expected = IncompleteSystemic<{ foo: { baz: { quux: number } } }> & + type Expected = IncompleteSystemic<"qux", { foo: { baz: { quux: number } } }> & DependsOn<Registrations, "qux", { foo: { baz: { quux: number } } }>; expectTypes<typeof system, Expected>().toBeEqual(); @@ -263,7 +266,10 @@ describe("systemic types", () => { "foo.bar": { component: { baz: number }; scoped: false }; qux: { component: number; scoped: false }; }; - type Expected = SystemicWithInvalidDependency<["foo.bar", { baz: string }, { baz: number }]>; + type Expected = SystemicWithInvalidDependency< + "qux", + ["foo.bar", { baz: string }, { baz: number }] + >; expectTypes<typeof system, Expected>().toBeEqual(); }); @@ -306,13 +312,13 @@ describe("systemic types", () => { .add("bar", { start: async (deps: { baz: { bar: string } }) => 42 }) .dependsOn({ component: "foo", destination: "baz" }); - type Expected = SystemicWithInvalidDependency<[string, unknown, unknown]>; + type Expected = SystemicWithInvalidDependency<"bar", [string, unknown, unknown]>; expectTypes<typeof system, Expected>().toBeEqual(); expectTypes< (typeof system)["start"], ( - error: "Destination of a dependency is unknown. Did you neglect to mark it 'as const'?", + error: `Destination of a dependency for component "bar" is unknown. Did you neglect to mark it 'as const'?`, expected: unknown, actual: unknown, ) => void