-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: improved type safety for extra param
BREAKING CHANGE: made the usage of fn or `to` exclusive. So they can't used both at the same time now.
- Loading branch information
Showing
4 changed files
with
99 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,96 +18,89 @@ npm install transmutant | |
|
||
## Usage | ||
|
||
### Basic Property Mapping | ||
### Direct Property Mapping | ||
|
||
```typescript | ||
import { transmute } from 'transmutant'; | ||
|
||
interface User { | ||
firstName: string; | ||
lastName: string; | ||
age: number; | ||
interface Source { | ||
email: string; | ||
} | ||
|
||
interface UserDTO { | ||
fullName: string; | ||
yearOfBirth: number; | ||
interface Target { | ||
contactEmail: string; | ||
} | ||
|
||
const schema = [ | ||
{ | ||
to: 'fullName', | ||
fn: ({ source }) => `${source.firstName} ${source.lastName}` | ||
}, | ||
{ | ||
to: 'yearOfBirth', | ||
fn: ({ source }) => new Date().getFullYear() - source.age | ||
} | ||
const schema: Schema<Source, Target>[] = [ | ||
{ from: 'email', to: 'contactEmail' } | ||
]; | ||
|
||
const user: User = { | ||
firstName: 'John', | ||
lastName: 'Doe', | ||
age: 30 | ||
}; | ||
const source: Source = { email: '[email protected]' }; | ||
const target = transmute(schema, source); | ||
|
||
const userDTO = transmute<User, UserDTO>(schema, user); | ||
// Result: { fullName: 'John Doe', yearOfBirth: 1994 } | ||
// Result: { contactEmail: '[email protected]' } | ||
``` | ||
|
||
### Direct Property Mapping | ||
### Using Custom Transmutation Functions | ||
|
||
```typescript | ||
interface Source { | ||
id: number; | ||
name: string; | ||
firstName: string; | ||
lastName: string; | ||
} | ||
|
||
interface Target { | ||
userId: number; | ||
userName: string; | ||
fullName: string; | ||
} | ||
|
||
const schema = [ | ||
{ from: 'id', to: 'userId' }, | ||
{ from: 'name', to: 'userName' } | ||
const schema: Schema<Source, Target>[] = [ | ||
{ | ||
to: 'fullName', | ||
fn: ({ source }) => `${source.firstName} ${source.lastName}` | ||
} | ||
]; | ||
|
||
const source: Source = { id: 1, name: 'John' }; | ||
const target = transmute<Source, Target>(schema, source); | ||
// Result: { userId: 1, userName: 'John' } | ||
const source: Source = { firstName: 'John', lastName: 'Doe' }; | ||
const target = transmute(schema, source); | ||
|
||
// Result: { fullName: 'John Doe' } | ||
``` | ||
|
||
### Using Extra Data | ||
|
||
```typescript | ||
interface Product { | ||
price: number; | ||
interface Source { | ||
city: string; | ||
country: string; | ||
} | ||
|
||
interface Target { | ||
location: string; | ||
} | ||
|
||
interface PricedProduct { | ||
finalPrice: number; | ||
interface Extra { | ||
separator: string; | ||
} | ||
|
||
const schema = [ | ||
const schema: Schema<Source, Target, Extra>[] = [ | ||
{ | ||
to: 'finalPrice', | ||
fn: ({ source, extra }) => source.price * (1 + extra?.taxRate) | ||
to: 'location', | ||
fn: ({ source, extra }) => | ||
`${source.city}, ${source.country}${extra?.separator}` | ||
} | ||
]; | ||
|
||
const product: Product = { price: 100 }; | ||
const pricedProduct = transmute<Product, PricedProduct>( | ||
schema, | ||
product, | ||
{ taxRate: 0.2 } | ||
); | ||
// Result: { finalPrice: 120 } | ||
const source: Source = { | ||
city: 'New York', | ||
country: 'USA' | ||
}; | ||
|
||
const target = transmute(schema, source, { separator: ' | ' }); | ||
|
||
// Result: { location: 'New York, USA | ' } | ||
``` | ||
|
||
## API Reference | ||
|
||
### `transmute<Source, Target>(schema, source, extra?)` | ||
### `transmute(schema, source, extra?)` | ||
|
||
Transmutes a source object into a target type based on the provided schema. | ||
|
||
|
@@ -117,29 +110,49 @@ Transmutes a source object into a target type based on the provided schema. | |
- `source`: Source object to transmute | ||
- `extra`: (Optional) Additional data to pass to transmutation functions | ||
|
||
#### Schema Options | ||
#### Schema Types | ||
|
||
Each schema entry must specify the target property and use either direct mapping OR a custom function: | ||
|
||
1. Direct mapping: | ||
```typescript | ||
{ | ||
type Schema<Source, Target, TExtra> = { | ||
/** Target property key */ | ||
to: keyof Target; | ||
from: keyof Source; | ||
} | ||
} & ( | ||
| { | ||
/** Source property key for direct mapping */ | ||
from: keyof Source; | ||
fn?: never; | ||
} | ||
| { | ||
/** Custom transmutation function */ | ||
fn: TransmuteFn<Source, TExtra>; | ||
from?: never; | ||
} | ||
); | ||
``` | ||
|
||
2. Custom transmutation: | ||
The `TransmuteFn` type is defined as: | ||
```typescript | ||
{ | ||
to: keyof Target; | ||
fn: (args: { source: Source; extra?: Extra }) => unknown; | ||
} | ||
type TransmuteFn<Source, TExtra> = (args: { | ||
source: Source; | ||
extra?: TExtra; | ||
}) => unknown; | ||
``` | ||
|
||
#### Behavior Notes | ||
|
||
- Direct mapping uses the `from` property to copy values directly from source to target | ||
- Custom functions receive the entire source object and optional extra data | ||
- If a direct mapping property is undefined or null, it will be set to `null` in the target object | ||
- Empty schemas result in an empty object | ||
- Each schema entry must use either `from` OR `fn`, but not both | ||
- The schema is processed sequentially, with each rule contributing to the final object | ||
|
||
## License | ||
|
||
MIT | ||
|
||
## Contributing | ||
|
||
Contributions are welcome! Please feel free to submit a Pull Request. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,37 @@ | ||
/** | ||
* Represents additional data that can be passed to mutation functions | ||
*/ | ||
export type Extra = Record<string, unknown> | ||
|
||
/** | ||
* Arguments passed to a mutation function | ||
* @template From - The source type being transmuted from | ||
*/ | ||
export interface TransmuteFnArgs<Source> { | ||
export type TransmuteFnArgs<Source, TExtra> = { | ||
/** The source object being transmuted */ | ||
source: Source | ||
/** Optional source property key */ | ||
from?: keyof Source | ||
/** Optional extra data to assist with transmutation */ | ||
extra?: Extra | ||
extra?: TExtra | ||
} | ||
|
||
/** | ||
* Function that performs a custom transmutation on a source object | ||
* @template From - The source type being transmuted from | ||
*/ | ||
export type TransmuteFn<Source> = (args: TransmuteFnArgs<Source>) => unknown | ||
export type TransmuteFn<Source, TExtra = unknown> = (args: TransmuteFnArgs<Source, TExtra>) => unknown | ||
|
||
/** | ||
* Defines how a property should be transmuted from source to target type | ||
* @template From - The source type being transmuted from | ||
* @template To - The target type being transmuted to | ||
*/ | ||
export type Schema<Source, Target> = | { | ||
export type Schema<Source, Target, TExtra = unknown> = { | ||
/** Target property key */ | ||
to: keyof Target | ||
} & ( | ||
| { | ||
/** Source property key for direct mapping */ | ||
from: keyof Source | ||
fn?: never | ||
} | ||
| { | ||
/** Custom transmutation function */ | ||
fn?: TransmuteFn<Source> | ||
} | { | ||
/** Target property key */ | ||
to: keyof Target | ||
/** Source property key for direct mapping */ | ||
from?: keyof Source | ||
/** Custom transmutation function */ | ||
fn: TransmuteFn<Source> | ||
fn: TransmuteFn<Source, TExtra> | ||
from?: never | ||
} | ||
) |