lorenzofox3 / dismoi

MIT License
14 stars 2 forks source link

Type problem when defining late dependencies with createProvider #4

Closed sybers closed 4 months ago

sybers commented 4 months ago

Hi ! I'm facing difficulties with dependencies not explicitly declared in createProvider which are used in other injectables.

Here's a code snippet which can be put in test.ts

  const provideMissingWithIntermediate = createProvider({
    injectables: {
      a: ({ value }: { value: number }) => value + 10,
      intermediate: () => '120'
    },
    api: ['a']
  });

  provideMissingWithIntermediate({
    // This should work right ?
    value: ({ intermediate }: { intermediate: string }) => Number(intermediate)
  })

The code below doesn't work, as dismoi doesn't seem to recognize the intermediate parameter used to construct the value dependency.

lorenzofox3 commented 4 months ago

Yes, technically there should not be any constraint on late bound factories (https://github.com/lorenzofox3/dismoi/blob/main/src/index.d.ts#L72), it is quite the opposite: they could bring a new set of constraints (see example below). Bu this gets contrived.

Please see the PR for the fix

 const provideMissingWithIntermediate = createProvider({
  injectables: {
    a: ({ value }: { value: number }) => value + 10,
    intermediate: () => '120'
  },
  api: ['a']
});

// the error here won't be caught (missing aMewConstraint)
provideMissingWithIntermediate({
  value: ({ intermediate, aNewConstraint }: { intermediate: string, aNewConstraint: number }) => Number(intermediate)
});

On the other hand, your example where the "external dep" also needs an injectable from the module itself looks like a code smell to me (some sort of cyclic dependencies)

If you have shared dependencies why not simply injecting them in both trees ?

// shared between several providers
const intermediate = () => '120';

const provideMissingWithIntermediate = createProvider({
  injectables: {
    a: ({ value }: { value: number }) => value + 10,
    intermediate // assuming it is needed in this module too
    //...
  },
  api: ['a']
});

const provideValue = createProvider({
  injectables: {
    intermediate,
    value: ({ intermediate }: { intermediate: string }) => Number(intermediate)
  },
  api: ['value']
});

provideMissingWithIntermediate({
  // This should work right ?
  value: () => provideValue().value
});