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

Use value from another field #41

Open
coler-j opened this issue Nov 15, 2020 · 5 comments
Open

Use value from another field #41

coler-j opened this issue Nov 15, 2020 · 5 comments

Comments

@coler-j
Copy link

coler-j commented Nov 15, 2020

Is it possible to use a field value, from another fields's value?

I.e.

export const someFactory = Factory.Sync.makeFactory<ISomeType>({
  id: uuidv4(),
  detailLink: Factory.each(i => `/${this.id}`)
})
@coler-j
Copy link
Author

coler-j commented Nov 15, 2020

It seems like the README language is trying to imply there is a functional way to do this:

Finally, you could instantiate a Derived<TOwner,TProperty> for the value of a property inside a Factory.makeFactory definition, but the type inference can't help you as much - you'll have to indicate the type of TOwner and TProperty.

@coler-j
Copy link
Author

coler-j commented Nov 15, 2020

Kind of an ugly interface, but I was able to do this via:

const _someFactory = Factory.Sync.makeFactory<ISomeType>({
  id: uuidv4(),
  detailLink: Factory.each(i => `/${this.id}`)
})

export const someFactory = _someFactory.withDerivation1(['id'], 'detailLink', id =>
 `/${id}`
)

I think it would make more sense if there was an interface like:

const someFactory = Factory.Sync.makeFactory<ISomeType>({
  id: uuidv4(),
  detailLink: Factory.relatedVal(item => `/${item.id}`) // Get access to values of current item
})

@willryan
Copy link
Owner

willryan commented Nov 17, 2022

Two years late, but I'm finally going through the backlog of issues a bit.

I do like the API you propose, but I'm not sure how to solve the ordering issues introduced. Say for example you defined id based on item.detailLink, as well as detailLink from item.id. That would be invalid and unresolvable. If we make the user specify an ordering, you end up with a complicated graph to specify, and awkward type help (if possible at all). I think it makes more sense to just derive based off of initial instances, but you can do it a little more succinctly than your example:

export const someFactory = Factory.Sync.makeFactory<ISomeType>({
  id: uuidv4(), // I'm assuming this returns a Generator and specifying the same uuid for all
  detailLink: 'TBD' // never used
}).withDerivation('detailLink', item => `/${item}`);

Of you could declare detailLink like:

  detailLink: Sync.each(() => { throw "never reached" }),

to really drive the point home.

I'd also be fine with introducing Sync.derivedLater() as a choice alongside Sync.each() and Sync.val(), which throws if you don't override it. (So you don't have to provide an initial value before deriving the value you actually want.)

@nidomiro
Copy link

nidomiro commented Nov 9, 2023

Unfortunately the approach with *Derivation introduces some bad sideeffects:

interface A {
  prop1: string;
  prop2: string;
  prop3: string;
}

interface B {
  a: A;
  b: string;
}

const factory2 = Factory.Sync.makeFactory<B>(() => {
  const value = "value";
  return {
    a: {} as A,
    b: value,
  };
}).withDerivation("a", (b) => ({
  prop1: b.b,
  prop2: b.b,
  prop3: b.b,
}));

console.log(inspect(factory2.build({ b: "b" }), false, 10, true));
// Correctly prints: `{ a: { prop1: 'b', prop2: 'b', prop3: 'b' }, b: 'b' }`

console.log(
  inspect(factory2.build({ b: "b", a: { prop1: "a" } }), false, 10, true)
);
// Incorrectly prints: `{ a: { func: [Function (anonymous)], prop1: 'a' }, b: 'b' }`

The result is the same when using withSelfDerivation

@nidomiro
Copy link

Further investigation shows that the func-property is the derivation function:

const deriveFunc = <T extends { b: string }>(b: T) => ({
  prop1: b.b,
  prop2: b.b,
  prop3: b.b,
});

const factory2 = Factory.Sync.makeFactory<B>(() => {
  const value = "value";
  return {
    a: {} as A,
    b: value,
  };
}).withDerivation("a", deriveFunc);

console.log(
  inspect(
    {
      "faultyResult.a": faultyResult.a,
      //@ts-expect-error
      "faultyResult.a.func": faultyResult.a.func,
      //@ts-expect-error
      "faultyResult.a.func.toString": faultyResult.a.func.toString(),
      //@ts-expect-error
      "faultyResult.a.func === deriveFunc": faultyResult.a.func === deriveFunc,
    },
    false,
    10,
    true
  )
);

prints:

{
  'faultyResult.a': { func: [Function: deriveFunc], prop1: 'a' },
  'faultyResult.a.func': [Function: deriveFunc],
  'faultyResult.a.func.toString': '(b) => ({\n    prop1: b.b,\n    prop2: b.b,\n    prop3: b.b,\n})',
  'faultyResult.a.func === deriveFunc': true
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants