Skip to content
This repository was archived by the owner on Apr 21, 2024. It is now read-only.
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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:

strategy:
matrix:
node-version: [14.x, 16.x, 17.x]
node-version: [14.x, '16.4.0', 17.x]

steps:
- uses: actions/checkout@v2
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { ... } from 'https://deno.land/x/lambda_ioc@[VERSION]/lambda-ioc/deno/in

```ts
import {
cc2ic, // Stands for "class-constructor to interface-constructor"
constructor,
createContainer,
func
Expand All @@ -46,7 +47,12 @@ function printNameAndAge(name: string, age: number) {
console.log(`${name} is aged ${age}`)
}

class Person {
interface Human {
age: number
name: readonly string
}

class Person implements Human {
constructor(
public readonly age: number,
public readonly name: string
Expand All @@ -60,6 +66,11 @@ const container = createContainer()
.register('fn', func(printNameAndAge, 'someName', 'someAge'))
// And constructors too
.register('Person', constructor(Person, 'someAge', 'someName'))
// We can do that directly, without having import `constructor`:
.registerConstructor('AnotherPerson', Person, 'someAge', 'someName')
// In case we want to register a "concrete" constructor to provide an
// abstract interface, we'll have to apply a small hack, using `cc2ic`:
.registerConstructor('Human', cc2ic<Human>()(Person), 'someAge', 'someName')
// We can "define groups" by using `:` as an infix, the group's name will be
// the first part of the string before `:`.
// Groups can be used in all "register" methods.
Expand Down
13 changes: 12 additions & 1 deletion lambda-ioc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { ... } from 'https://deno.land/x/lambda_ioc@[VERSION]/lambda-ioc/deno/in

```ts
import {
cc2ic, // Stands for "class-constructor to interface-constructor"
constructor,
createContainer,
func
Expand All @@ -46,7 +47,12 @@ function printNameAndAge(name: string, age: number) {
console.log(`${name} is aged ${age}`)
}

class Person {
interface Human {
age: number
name: readonly string
}

class Person implements Human {
constructor(
public readonly age: number,
public readonly name: string
Expand All @@ -60,6 +66,11 @@ const container = createContainer()
.register('fn', func(printNameAndAge, 'someName', 'someAge'))
// And constructors too
.register('Person', constructor(Person, 'someAge', 'someName'))
// We can do that directly, without having import `constructor`:
.registerConstructor('AnotherPerson', Person, 'someAge', 'someName')
// In case we want to register a "concrete" constructor to provide an
// abstract interface, we'll have to apply a small hack, using `cc2ic`:
.registerConstructor('Human', cc2ic<Human>()(Person), 'someAge', 'someName')
// We can "define groups" by using `:` as an infix, the group's name will be
// the first part of the string before `:`.
// Groups can be used in all "register" methods.
Expand Down
28 changes: 28 additions & 0 deletions lambda-ioc/deno/combinators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ export function constructor<
}
}

// Class-Constructor to Interface-Constructor
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function cc2ic<I>(): <CC extends new (...args: any[]) => I>(cc: CC) => AsInterfaceCtor<I, CC> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return identity as <C extends I, CC extends new (...args: any[]) => C>(
cc: CC,
) => AsInterfaceCtor<I, CC>
}

// -----------------------------------------------------------------------------
// Private Types
// -----------------------------------------------------------------------------
Expand All @@ -125,3 +134,22 @@ type SyncFuncContainer<
Extract<Zip<TSyncDependencies, TParams>, readonly [ContainerKey, any][]>
>
>

// Converts a "class constructor" type to an "interface constructor" type
type AsInterfaceCtor<
I, // The interface we are interested on
// We have to pass CC to know its constructor parameters
// eslint-disable-next-line @typescript-eslint/no-explicit-any
CC extends new (...args: any[]) => I = new () => I,
> = CC extends new (...args: infer A) => infer C
? C extends I
? new (...args: A) => I
: never
: never

// -----------------------------------------------------------------------------
// Private Functions
// -----------------------------------------------------------------------------

// Helper function, here to avoid creating too many anonymous lambdas
const identity = (x: unknown) => x
8 changes: 7 additions & 1 deletion lambda-ioc/deno/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ export {
type WritableContainer,
createContainer,
} from './container.ts';
export { asyncSingleton, constructor, func, singleton } from './combinators.ts';
export {
asyncSingleton,
cc2ic,
constructor,
func,
singleton,
} from './combinators.ts';
20 changes: 10 additions & 10 deletions lambda-ioc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coderspirit/lambda-ioc",
"version": "0.7.1",
"version": "0.8.0",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/cjs/index.d.ts",
Expand Down Expand Up @@ -40,17 +40,17 @@
"devDependencies": {
"@types/jest": "^27.4.1",
"@types/node": "^14.14.37",
"@typescript-eslint/eslint-plugin": "^5.12.1",
"@typescript-eslint/parser": "^5.12.1",
"eslint": "^8.10.0",
"eslint-config-prettier": "^8.4.0",
"eslint-plugin-jest": "^26.1.1",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"eslint": "^8.13.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-jest": "^26.1.4",
"eslint-plugin-node": "^11.1.0",
"jest": "^27.5.1",
"prettier": "^2.5.1",
"ts-jest": "^27.1.3",
"ts-node": "^10.5.0",
"typescript": "^4.5.5"
"prettier": "^2.6.2",
"ts-jest": "^27.1.4",
"ts-node": "^10.7.0",
"typescript": "^4.6.3"
},
"engines": {
"node": ">=14.0.0"
Expand Down
33 changes: 33 additions & 0 deletions lambda-ioc/src/__tests__/container.regressions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { cc2ic } from '../combinators'
import { createContainer } from '..'

interface IA {
f: () => string
}

class A implements IA {
constructor(private readonly s: string) {}
public f(): string {
return this.s
}
}

class B {
constructor(private readonly a: IA) {}
public g(): IA {
return this.a
}
}

describe('container regressions', () => {
describe('registerConstructor', () => {
it('allows to register constructors they are depended upon through interfaces', () => {
const container = createContainer()
.registerValue('greeting', 'hello')
.registerConstructor('A', cc2ic<IA>()(A), 'greeting') // We have to rely on cc2ic
.registerConstructor('B', B, 'A')

expect(container.resolve('B').g().f()).toBe('hello')
})
})
})
28 changes: 28 additions & 0 deletions lambda-ioc/src/combinators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ export function constructor<
}
}

// Class-Constructor to Interface-Constructor
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function cc2ic<I>(): <CC extends new (...args: any[]) => I>(cc: CC) => AsInterfaceCtor<I, CC> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return identity as <C extends I, CC extends new (...args: any[]) => C>(
cc: CC,
) => AsInterfaceCtor<I, CC>
}

// -----------------------------------------------------------------------------
// Private Types
// -----------------------------------------------------------------------------
Expand All @@ -125,3 +134,22 @@ type SyncFuncContainer<
Extract<Zip<TSyncDependencies, TParams>, readonly [ContainerKey, any][]>
>
>

// Converts a "class constructor" type to an "interface constructor" type
type AsInterfaceCtor<
I, // The interface we are interested on
// We have to pass CC to know its constructor parameters
// eslint-disable-next-line @typescript-eslint/no-explicit-any
CC extends new (...args: any[]) => I = new () => I,
> = CC extends new (...args: infer A) => infer C
? C extends I
? new (...args: A) => I
: never
: never

// -----------------------------------------------------------------------------
// Private Functions
// -----------------------------------------------------------------------------

// Helper function, here to avoid creating too many anonymous lambdas
const identity = (x: unknown) => x
8 changes: 7 additions & 1 deletion lambda-ioc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@ export {
type WritableContainer,
createContainer,
} from './container'
export { asyncSingleton, constructor, func, singleton } from './combinators'
export {
asyncSingleton,
cc2ic,
constructor,
func,
singleton,
} from './combinators'
Loading