-
-
Notifications
You must be signed in to change notification settings - Fork 36
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
Is there a way to specify the type when extending factories? #71
Comments
I had similar issues, I currently do type casting to solve this const userFactory = Factory.define<User>(() => {
id: 42,
isAdmin: false
});
const adminFactory = (userFactory as Factory<AdminUser>).withParams({
adminPrivileges: []
}) This way, you can still get the benefit from the typings. It would be nice if the library support this though. |
Since the approach in #75 doesn’t work I’ve tried out to implement an |
I just wanted to update here that I have been playing around with a complete rewrite of Fishery that I think will address this while also improving the types throughout. Here's a preview of the API I'm working on: type User = { name: string; admin: boolean };
type AdminUser = User & { admin: true };
type SavedUser = User & { id: number };
const factory = Fishery.define({
build: () =>
({
name: 'Bob',
admin: false,
} as User),
create: user => Promise.resolve({ ...user, id: 1 }),
traits: {
admin: () => ({ admin: true as const }),
},
});
const user = factory.build({ admin: true, name: 'Susan' });
user // typed such that it knows about params, typed as User & { admin: true, name: string };
// create
// if no `create` function defined in the factory, then factory.create() is a type error instead of existing runtime error
factory.create({}); // returns Promise<User & { id: number }> (AKA, a SavedUser)
// extend
const userWithPostsFactory = factory.extend({ posts: [] as Post[] });
const userWithPosts = userWithPostsFactory.build({
posts: [{ id: 1, body: 'post' }],
});
userWithPosts // typed as User & { posts: Post[] }
// traits
const adminFactory = factory.admin(); // builds objects of type User & { admin: true }
const admin: AdminUser = adminFactory.build({}); I already have the types for all of the above built but have yet to implement the code, and there are a lot of missing pieces. I'm planning to dedicate a bit of time in the next month or so to work on this. |
Nice, I’m already looking forward to the rewrite 🥳. I also really like that traits can now be specified without subclassing 👍. const userWithPostsFactory = factory.extend({ posts: [] as Post[] }); Would it be also possible to specify the exact type instead of using a const assertion here? E.g. const userWithPostsFactory = factory.extend<UserWithPosts>({ posts: [] }); While talking about traits, I’ve recently had very often the need for traits that would produce invalid data, e.g. for testing runtime validation logic. Since the data structures are quite big and I only want to test the absence of e.g. one property I would love to re-use the existing factories. Currently I’m using a combination of const largeDataStructureFactory = Factory.define<LargeDataStructure>(/* ... */);
function largeDataStructureWithoutProperyFooFactory(): unknown {
const data = largeDataStructureFactory.build();
return dotProp.remove(data, 'foo');
} Do you think something like that would also fit into fishery? |
@stevehanson I've seen you mentioned the rewrite on a few issues—how's progress coming with that? I'm trying to assess whether to add fishery to our new product—it looks great, but a few of the flexibility-related issues would be very handy. |
@samtgarson thanks for checking in. The new version is still a little ways out. I would like to have a beta by the end of the year or early next year, but I can't make any promises as I basically work on this project in bursts when I have the time. The current version of Fishery is pretty stable and should work great for most cases, but it does lack flexibility for some more complex scenarios, like you mentioned. |
This isn't as nice as what @lo1tuma recommended at the top, but I'm using this pattern: interface BaseUser {
id: number;
isAdmin: boolean;
}
interface NormalUser extends BaseUser {
isAdmin: false;
}
interface AdminUser extends BaseUser {
isAdmin: true;
adminPrivileges: string[];
}
type User = NormalUser | AdminUser;
const baseUserFactory = Factory.define<BaseUser>( () => ( {
id: 42,
isAdmin: false,
} ) );
interface TransientParams {
baseUser: BaseUser;
}
const adminUserFactory = Factory.define<AdminUser, TransientParams>( ( { transientParams: { baseUser } } ) => ( {
...( baseUser || baseUserFactory.build( { isAdmin: true } ) ),
isAdmin: true,
adminPrivileges: [],
} ) );
const baseUser = baseUserFactory.build();
const adminUser = adminUserFactory.build();
const adminUserWithBaseUser = adminUserFactory.build( {}, { transient: { baseUser } } ); I'm not extending using Fishery semantics, but via simple object spreading. As as result, my |
Given the following types:
Is there a way to create a factory for the type
User
and then a factory for the typeAdminUser
which re-uses theUser
factory but has the correct type information, so theAdminUser
factory returns objects of the typeAdminUser
?As far as I understand we can only extend the factories like so:
In this case
adminFactory
returns the typeUser
.My suggestion would to make the following work:
So that
adminFactory
always returns objects of typeAdminUser
and only accepts params ofDeepPartial<AdminUser>
.The text was updated successfully, but these errors were encountered: