Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ You can rename dependencies passed to a components start function by specifying
const system = systemic()
.add('config', initConfig())
.add('mongo', initMongo())
.dependsOn({ component: 'config', destination: 'options' } as const);
.dependsOn({ component: 'config', destination: 'options' });
```

If you want to inject a property or subdocument of the dependency thing you can also express this with a dependency mapping
Expand All @@ -279,11 +279,10 @@ If you want to inject a property or subdocument of the dependency thing you can
const system = systemic()
.add('config', initConfig())
.add('mongo', initMongo())
.dependsOn({ component: 'config', source: 'mongo' } as const);
.dependsOn({ component: 'config', source: 'mongo' });
```

Now `config.mongo` will be injected as `config` instead of the entire configuration object.
Because of the way typescript narrowing of object properties works, mappings need to be added as constants. Otherwise `@ilpt/systemic-ts` is not able to validate the dependency.

#### Scoped Dependencies

Expand Down
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### What's changed

- No changelog has been added to this release
- Remove the need for marking a mapping dependency `as const`

---

Expand Down
2 changes: 1 addition & 1 deletion src/types/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ type ValidateMappingDependency<
TDependencies extends Record<string, unknown>,
TMapping extends { component: string; destination: string; source?: string },
> = IsDestinationOrSourceUnbound<TMapping> extends true
? [DependencyValidationError<string, unknown, unknown>] // Dependency not created as constant
? [DependencyValidationError<string, unknown, unknown>] // Dependency cannot be determined
: [PropAt<TDependencies, DependencyDestinationOf<TMapping>>] extends [never]
? [] // Unexpected dependency
: [Injected<TSystemic, TCurrent, TMapping>] extends [
Expand Down
6 changes: 3 additions & 3 deletions src/types/systemic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export type DependsOn<
* When name and type of the dependencies match those available in the system, the dependency can be added by name.
* When a dependency is named differently in the system or only part of a component is required as a dependency, a MappingDependsOnOption can be used.
*/
dependsOn: <TNames extends DependsOnOption<Omit<TSystemic, TCurrent>>[]>(
dependsOn: <const TNames extends DependsOnOption<Omit<TSystemic, TCurrent>>[]>(
...names: TNames
) => ValidateDependencies<TSystemic, TCurrent, TDependencies, TNames> extends [
infer First extends DependencyValidationError<any, any, any>,
Expand All @@ -171,7 +171,7 @@ export type SystemicWithInvalidDependency<
> = {
[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'?`
? `Destination of a dependency for component "${TCurrent}" is unknown.`
: `Dependency "${TError[0]}" on component "${TCurrent}" is not of the required type`,
expected: TError[1],
actual: TError[2],
Expand All @@ -195,7 +195,7 @@ type SystemicBuildDefaultComponent<
* When name and type of the dependencies match those available in the system, the dependency can be added by name.
* When a dependency is named differently in the system or only part of a component is required as a dependency, a MappingDependsOnOption can be used.
*/
dependsOn: <TNames extends DependsOnOption<Omit<TSystemic, TCurrent>>[]>(
dependsOn: <const TNames extends DependsOnOption<Omit<TSystemic, TCurrent>>[]>(
...names: TNames
) => ValidateDependencies<TSystemic, TCurrent, EmptyObject, TNames> extends [
infer First extends DependencyValidationError<any, any, any>,
Expand Down
10 changes: 5 additions & 5 deletions test/systemic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ describe("systemic", () => {
const system = systemic()
.add("foo", foo)
.add("bar", bar)
.dependsOn({ component: "foo", destination: "baz" } as const);
.dependsOn({ component: "foo", destination: "baz" });

await system.start();

Expand All @@ -349,7 +349,7 @@ describe("systemic", () => {
const system = systemic()
.add("foo", foo)
.add("bar", bar)
.dependsOn({ component: "foo", destination: "baz" } as const);
.dependsOn({ component: "foo", destination: "baz" });
await system.start();
expect(bar.state.dependencies).toEqual({ baz: "foo" });
});
Expand All @@ -359,7 +359,7 @@ describe("systemic", () => {
const system = systemic()
.add("foo", foo)
.add("bar")
.dependsOn({ component: "foo", destination: "baz" } as const);
.dependsOn({ component: "foo", destination: "baz" });
const components = await system.start();
expect(components).toEqual({ foo: "foo", bar: { baz: "foo" } });
});
Expand All @@ -370,7 +370,7 @@ describe("systemic", () => {
const system = systemic()
.add("foo", foo)
.add("bar", bar)
.dependsOn({ component: "foo", source: "qux" } as const);
.dependsOn({ component: "foo", source: "qux" });
await system.start();
expect(bar.state.dependencies).toEqual({ foo: "baz" });
});
Expand All @@ -381,7 +381,7 @@ describe("systemic", () => {
const system = systemic()
.add("foo", foo, { scoped: true })
.add("bar", bar)
.dependsOn({ component: "foo", source: "" } as const);
.dependsOn({ component: "foo", source: "" });
await system.start();
expect(bar.state.dependencies).toEqual({ foo: { qux: "baz" } });
});
Expand Down
44 changes: 3 additions & 41 deletions test/types/systemic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ describe("systemic types", () => {
const system = mockSystemic()
.add("foo.bar", { start: async (deps: EmptyObject) => ({ baz: 42 }) })
.add("qux", { start: async (deps: { baz: { baz: number } }) => 42 })
.dependsOn({ component: "foo.bar", destination: "baz" } as const);
.dependsOn({ component: "foo.bar", destination: "baz" });

type Registrations = {
"foo.bar": { component: { baz: number }; scoped: false };
Expand All @@ -307,7 +307,7 @@ describe("systemic types", () => {
const system = mockSystemic()
.add("foo", { start: async (deps: EmptyObject) => ({ baz: "qux" }) }, { scoped: true })
.add("bar", { start: async (deps: { foo: { baz: string } }) => 42 })
.dependsOn({ component: "foo", source: "" } as const);
.dependsOn({ component: "foo", source: "" });

type Registrations = {
foo: { component: { baz: string }; scoped: true };
Expand All @@ -322,7 +322,7 @@ describe("systemic types", () => {
const system = mockSystemic()
.add("foo", { start: async (deps: EmptyObject) => ({ baz: "qux" }) }, { scoped: true })
.add("bar", { start: async (deps: { foo: { baz: string } }) => 42 })
.dependsOn({ component: "foo", source: "test123" } as const);
.dependsOn({ component: "foo", source: "test123" });

type Registrations = {
foo: { component: { baz: string }; scoped: true };
Expand All @@ -333,44 +333,6 @@ describe("systemic types", () => {
expectTypes<typeof system, Expected>().toBeEqual();
});

it("is a systemic with a dependency not marked as const", () => {
const system = mockSystemic()
.add("foo", { start: async (deps: EmptyObject) => ({ bar: "bar" }) })
.add("bar", { start: async (deps: { baz: { bar: string } }) => 42 })
.dependsOn({ component: "foo", destination: "baz" });

type Expected = SystemicWithInvalidDependency<"bar", [string, unknown, unknown]>;

expectTypes<typeof system, Expected>().toBeEqual();
expectTypes<
(typeof system)["start"],
(
error: `Destination of a dependency for component "bar" is unknown. Did you neglect to mark it 'as const'?`,
expected: unknown,
actual: unknown,
) => void
>().toBeEqual();
});

it("is a systemic with a dependency on a default component not marked as const", () => {
const system = mockSystemic()
.add("foo", { start: async (deps: EmptyObject) => ({ bar: "bar" }) })
.add("bar")
.dependsOn({ component: "foo", destination: "baz" });

type Expected = SystemicWithInvalidDependency<"bar", [string, unknown, unknown]>;

expectTypes<typeof system, Expected>().toBeEqual();
expectTypes<
(typeof system)["start"],
(
error: `Destination of a dependency for component "bar" is unknown. Did you neglect to mark it 'as const'?`,
expected: unknown,
actual: unknown,
) => void
>().toBeEqual();
});

it("is a systemic with an injected dependency that extends the expected type", () => {
const system = mockSystemic()
.add("foo", { start: async (deps: EmptyObject) => ({ foo: "bar" }) })
Expand Down
Loading