https://github.com/nertzy/ts-factory
A way to build factories for TypeScript objects
https://github.com/nertzy/ts-factory
factory typescript
Last synced: about 1 year ago
JSON representation
A way to build factories for TypeScript objects
- Host: GitHub
- URL: https://github.com/nertzy/ts-factory
- Owner: nertzy
- License: mit
- Created: 2018-02-03T00:20:22.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2025-04-07T12:12:59.000Z (about 1 year ago)
- Last Synced: 2025-04-10T07:18:21.012Z (about 1 year ago)
- Topics: factory, typescript
- Language: TypeScript
- Size: 484 KB
- Stars: 8
- Watchers: 2
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ts-factory
A way to build factories for TypeScript objects
[](https://github.com/nertzy/ts-factory/actions/workflows/node.js.yml)
[](https://www.npmjs.com/package/ts-factory)
[](https://github.com/nertzy/ts-factory/blob/master/LICENSE)
## Installation
```
$ npm install ts-factory
```
```
$ yarn add ts-factory
```
## buildFactory
```typescript
buildFactory(defaultObject: T): (overrides?: Partial) => T
```
`buildFactory` takes a type parameter and a default object of that type. It returns a function that takes an optional partial object of field overrides.
### Example
Consider the following types:
```typescript
interface Author {
id?: number;
name: string;
email: string;
}
interface Comment {
id?: number;
author: Author;
text: string;
}
interface BlogPost {
id?: number;
author: Author;
title: string;
body: string;
comments: Comment[];
}
```
We can make a factory for Author by calling `buildFactory` with a default object. Note that optional fields may be omitted.
```typescript
import { buildFactory } from "ts-factory";
export const buildAuthor = buildFactory({
name: "Grant Hutchins",
email: "granthutchins@example.com"
});
```
We can now use the `buildAuthor()` function to construct Author instances.
```typescript
buildAuthor();
// returns {
// name: "Grant Hutchins",
// email: "granthutchins@example.com"
// }
buildAuthor({});
// returns {
// name: "Grant Hutchins",
// email: "granthutchins@example.com"
// }
buildAuthor({name: "Mr. Hutchins"});
// returns {
// name: "Mr. Hutchins",
// email: "granthutchins@example.com"
// }
buildAuthor({id: 1});
// returns {
// id: 1,
// name: "Grant Hutchins",
// email: "granthutchins@example.com"
// }
```
We can now use `buildAuthor` to make it easier to build a factory for Comment.
```typescript
import { buildFactory } from "ts-factory";
export const buildComment = buildFactory({
author: buildAuthor(),
text: "myText"
});
```
Now we can easily build a Comment, knowing that its Author will also be valid.
```typescript
buildComment();
// returns {
// author: {
// name: "Grant Hutchins",
// email: "granthutchins@example.com"
// },
// text: "myText"
// }
buildComment({});
// returns {
// author: {
// name: "Grant Hutchins",
// email: "granthutchins@example.com"
// },
// text: "myText"
// }
buildComment({id: 1});
// returns {
// id: 1,
// author: {
// name: "Grant Hutchins",
// email: "granthutchins@example.com"
// },
// text: "myText"
// }
const anotherAuthor = buildAuthor({
id: 2,
name: "Another Author",
email: "another_author@example.com",
});
buildComment({author: anotherAuthor});
// returns {
// author: {
// id: 2,
// name: "Another Author",
// email: "another_author@example.com"
// },
// text: "myText"
// }
```
We can use `buildComment` to make it easier to build the default array of comments in `buildBlogPost`.
```typescript
export const buildBlogPost = buildFactory({
author: buildAuthor(),
title: "myTitle",
body: "myBody",
comments: [buildComment()]
});
```
Now you can confidently build a BlogPost with as much or as little data as you want.
```typescript
buildBlogPost();
// returns {
// author: {
// name: "Grant Hutchins",
// email: "granthutchins@example.com"
// },
// title: "myTitle",
// body: "myBody",
// comments: [
// {
// author: {
// name: "Grant Hutchins",
// email: "granthutchins@example.com",
// text: "myText"
// }
// }
// ]
// }
buildBlogPost({
id: 3,
comments: []
});
// returns {
// id: 3,
// author: {
// name: "Grant Hutchins",
// email: "granthutchins@example.com"
// },
// title: "myTitle",
// body: "myBody",
// comments: []
// }
buildBlogPost({
id: 3,
author: buildAuthor({id: 4}),
comments: [
buildComment({
id: 5,
author: buildAuthor({
id: 6
})
}),
buildComment({
author: buildAuthor({
id: 7
})
}),
]
});
// returns {
// id: 3,
// author: {
// id: 4,
// name: "Grant Hutchins",
// email: "granthutchins@example.com"
// },
// title: "myTitle",
// body: "myBody",
// comments: [
// {
// id: 5,
// author: {
// id: 6,
// name: "Grant Hutchins",
// email: "granthutchins@example.com",
// },
// text: "myText"
// },
// {
// author: {
// id: 7,
// name: "Grant Hutchins",
// email: "granthutchins@example.com",
// },
// text: "myText"
// }
// ]
// }
```
## FAQs
Isn't this just currying one argument to Object.assign?
> Yes.
Why can't I just do that myself?
> You can.
Then what's the point?
> The parameterized type argument helps guide you to get the types of the default objects and the overrides correct. It's not much, but it does help a small bit.
OK, how does it help?
> One example: if you add a required field to an interface that is heavily used throughout your test suite, you can just go in and add a value for this new field to its factory's default object and now all of your tests that use the factory will compile.