Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add generic parameter to AwilixContainer interface #169

Merged
merged 1 commit into from
Feb 28, 2020
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1335,10 +1335,10 @@ because they depend on Node-specific packages.

# Contributing

Clone repo, run `npm i` to install all dependencies, run `npm run build` to create an initial build, and then
`npm run test -- --watchAll` to start writing code.
Clone repo, run `yarn` to install all dependencies, run `yarn build` to create an initial build, and then
`yarn test --watchAll` to start writing code.

For code coverage, run `npm run cover`.
For code coverage, run `yarn cover`.

If you submit a PR, please aim for 100% code coverage and no linting errors.
Travis will fail if there are linting errors. Thank you for considering
Expand Down
9 changes: 7 additions & 2 deletions examples/typescript/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ import { createContainer, asClass, InjectionMode } from '../../../src/awilix'
import TestService from './services/TestService'
import DependentService from './services/DependentService'

interface ICradle {
testService: TestService
depService: DependentService
}

// Create the container
const container = createContainer({
const container = createContainer<ICradle>({
injectionMode: InjectionMode.CLASSIC
})

Expand All @@ -15,7 +20,7 @@ container.register({
})

// Resolve a dependency using the cradle.
let dep1: DependentService = container.cradle.depService
let dep1 = container.cradle.depService
// Resolve a dependency using `resolve`
let dep2 = container.resolve<DependentService>('depService')

Expand Down
36 changes: 18 additions & 18 deletions src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { AwilixResolutionError, AwilixTypeError } from './errors'
* The container returned from createContainer has some methods and properties.
* @interface AwilixContainer
*/
export interface AwilixContainer {
export interface AwilixContainer<Cradle extends object = any> {
/**
* Options the container was configured with.
*/
Expand All @@ -30,7 +30,7 @@ export interface AwilixContainer {
* The proxy injected when using `PROXY` injection mode.
* Can be used as-is.
*/
readonly cradle: any
readonly cradle: Cradle
/**
* Getter for the rolled up registrations that merges the container family tree.
*/
Expand All @@ -42,7 +42,7 @@ export interface AwilixContainer {
/**
* Creates a scoped container with this one as the parent.
*/
createScope(): AwilixContainer
createScope<T extends object = any>(): AwilixContainer<Cradle & T>
/**
* Used by `util.inspect`.
*/
Expand All @@ -67,7 +67,7 @@ export interface AwilixContainer {
/**
* Pairs resolvers to registration names and registers them.
*/
register(nameAndRegistrationPair: NameAndRegistrationPair): this
register(nameAndRegistrationPair: NameAndRegistrationPair<Cradle>): this
/**
* Resolves the registration with the given name.
*
Expand Down Expand Up @@ -122,23 +122,23 @@ export interface ResolveOptions {
/**
* Cache entry.
*/
export interface CacheEntry {
export interface CacheEntry<T = any> {
/**
* The resolver that resolved the value.
*/
resolver: Resolver<any>
resolver: Resolver<T>
/**
* The resolved value.
*/
value: any
value: T
}

/**
* Register a Registration
* @interface NameAndRegistrationPair
*/
export interface NameAndRegistrationPair {
[key: string]: Resolver<any>
export type NameAndRegistrationPair<T> = {
[U in keyof T]?: Resolver<T[U]>
}

/**
Expand Down Expand Up @@ -194,10 +194,10 @@ const ROLL_UP_REGISTRATIONS = Symbol('rollUpRegistrations')
* @return {object}
* The container.
*/
export function createContainer(
export function createContainer<T extends object = any, U extends object = any>(
options?: ContainerOptions,
parentContainer?: AwilixContainer
): AwilixContainer {
parentContainer?: AwilixContainer<U>
): AwilixContainer<T> {
options = {
injectionMode: InjectionMode.PROXY,
...options
Expand All @@ -221,7 +221,7 @@ export function createContainer(
* knowing where they come from. I call it the "cradle" because
* it is where registered things come to life at resolution-time.
*/
const cradle: { [key: string]: any } = new Proxy(
const cradle = new Proxy(
{
[util.inspect.custom]: inspectCradle
},
Expand Down Expand Up @@ -274,12 +274,12 @@ export function createContainer(
return undefined
}
}
)
) as T

// The container being exposed.
const container = {
options,
cradle: cradle as any,
cradle,
inspect,
cache: new Map<string | symbol, CacheEntry>(),
loadModules,
Expand Down Expand Up @@ -360,14 +360,14 @@ export function createContainer(
* @return {object}
* The scoped container.
*/
function createScope(): AwilixContainer {
return createContainer(options, container)
function createScope<P extends object>(): AwilixContainer<P & T> {
return createContainer(options, container as AwilixContainer<T>)
}

/**
* Adds a registration for a resolver.
*/
function register(arg1: any, arg2: any): AwilixContainer {
function register(arg1: any, arg2: any): AwilixContainer<T> {
const obj = nameValueToObject(arg1, arg2)
const keys = [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]

Expand Down
19 changes: 12 additions & 7 deletions src/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ export const RESOLVER = Symbol('Awilix Resolver Config')
*
* @type {Function}
*/
export type InjectorFunction = (container: AwilixContainer) => object
export type InjectorFunction = <T extends object>(
container: AwilixContainer<T>
) => object

/**
* A resolver object returned by asClass(), asFunction() or asValue().
*/
export interface Resolver<T> extends ResolverOptions<T> {
resolve(container: AwilixContainer): T
resolve<U extends object>(container: AwilixContainer<U>): T
}

/**
Expand Down Expand Up @@ -335,7 +337,10 @@ function updateResolver<T, A extends Resolver<T>, B>(
* @param {Object} locals
* @return {Function}
*/
function wrapWithLocals(container: AwilixContainer, locals: any) {
function wrapWithLocals<T extends object>(
container: AwilixContainer<T>,
locals: any
) {
return function wrappedResolve(name: string, resolveOpts: ResolveOptions) {
if (name in locals) {
return locals[name]
Expand All @@ -353,8 +358,8 @@ function wrapWithLocals(container: AwilixContainer, locals: any) {
* @param {Function} injector
* @return {Proxy}
*/
function createInjectorProxy(
container: AwilixContainer,
function createInjectorProxy<T extends object>(
container: AwilixContainer<T>,
injector: InjectorFunction
) {
const locals = injector(container) as any
Expand Down Expand Up @@ -444,9 +449,9 @@ function generateResolve(fn: Function, dependencyParseTarget?: Function) {
const dependencies = parseDependencies(dependencyParseTarget)

// Use a regular function instead of an arrow function to facilitate binding to the resolver.
return function resolve(
return function resolve<T extends object>(
this: BuildResolver<any>,
container: AwilixContainer
container: AwilixContainer<T>
) {
// Because the container holds a global reolutionMode we need to determine it in the proper order of precedence:
// resolver -> container -> default value
Expand Down