transmutant
Advanced tools
Comparing version 3.1.0 to 4.0.0
@@ -13,2 +13,2 @@ import { Schema } from './types'; | ||
*/ | ||
export declare const transmute: <Source, Target, Extra>(schema: Schema<Source, Target, Extra>[], source: Source, extra?: Extra) => Target; | ||
export declare const transmute: <Source, Target, Extra = undefined>(schema: Schema<Source, Target, Extra>[], source: Source, extra?: Extra) => Target; |
@@ -32,4 +32,9 @@ "use strict"; | ||
const isFunction = typeof from === 'function'; | ||
const extraData = extra ? { extra } : {}; | ||
const value = isFunction ? from({ source, ...extraData }) : source[from]; | ||
// Only include extra in args if it's defined | ||
const args = extra === undefined | ||
? { source } | ||
: { source, extra }; // Type assertion needed due to conditional type | ||
const value = isFunction | ||
? from(args) | ||
: source[from]; | ||
return { | ||
@@ -36,0 +41,0 @@ ...acc, |
@@ -6,3 +6,3 @@ /** | ||
*/ | ||
export type TransmuteFnArgs<Source, Extra> = Extra extends undefined | never ? { | ||
export type TransmuterArgs<Source, Extra> = Extra extends undefined ? { | ||
source: Source; | ||
@@ -20,3 +20,3 @@ } : { | ||
*/ | ||
export type TransmuteFn<Source, Target, TargetKey extends keyof Target, Extra> = (args: TransmuteFnArgs<Source, Extra>) => Target[TargetKey]; | ||
export type Transmuter<Source, Target, TargetKey extends keyof Target, Extra = undefined> = (args: TransmuterArgs<Source, Extra>) => Target[TargetKey]; | ||
/** | ||
@@ -37,5 +37,5 @@ * Get keys of Source that have values assignable to Target[TargetKey] | ||
to: TargetKey; | ||
from: AssignableKeys<Source, Target, TargetKey> | TransmuteFn<Source, Target, TargetKey, Extra>; | ||
from: AssignableKeys<Source, Target, TargetKey> | Transmuter<Source, Target, TargetKey, Extra>; | ||
}; | ||
}[keyof Target]; | ||
export {}; |
{ | ||
"name": "transmutant", | ||
"version": "3.1.0", | ||
"version": "4.0.0", | ||
"main": "dist/index.js", | ||
@@ -5,0 +5,0 @@ "types": "dist/index.d.ts", |
221
README.md
@@ -13,3 +13,3 @@ # 🧬 Transmutant 🧬 | ||
- 🔒 **Type-safe**: Full TypeScript support with strong type inference | ||
- 🎯 **Flexible mapping**: Direct property mapping or custom transmutation functions | ||
- 🎯 **Flexible mapping**: Direct property mapping or custom transmuter functions | ||
- ⚡ **High performance**: Minimal overhead and zero dependencies | ||
@@ -29,3 +29,3 @@ - 🔄 **Extensible**: Support for custom transmutation logic and external data | ||
```typescript | ||
import { transmute, Schema } from 'transmutant'; | ||
import { Schema, transmute } from 'transmutant' | ||
@@ -52,8 +52,8 @@ // Source type | ||
{ | ||
from: 'email', | ||
to: 'contactEmail' | ||
to: 'contactEmail', | ||
from: 'email' | ||
} | ||
]; | ||
] | ||
// Transmut the object | ||
// Transmute the object | ||
const user: User = { | ||
@@ -63,5 +63,5 @@ firstName: 'John', | ||
email: 'john@example.com' | ||
}; | ||
} | ||
const userDTO = transmute(schema, user); | ||
const userDTO = transmute(schema, user) | ||
// Result: { fullName: 'John Doe', contactEmail: 'john@example.com' } | ||
@@ -74,13 +74,13 @@ ``` | ||
A schema is an array of transmutation rules that define how properties should be mapped from the source to the target type. Each rule specifies the target property key and either a source property key for direct mapping or a transmutation function that produces the correct type for that target property. | ||
A schema is an array of transmutation rules that define how properties should be mapped from the source to the target | ||
type. Each rule specifies: | ||
- The target property key (`to`) | ||
- Either a source property key for direct mapping or a transmuter function (`from`) | ||
```typescript | ||
type Schema<Source, Target, Extra = unknown> = { | ||
[TargetKey in keyof Target]: { | ||
/** Target property key */ | ||
to: TargetKey | ||
/** Source property key for direct mapping or a custom transmutation function */ | ||
from: keyof Source | TransmuteFn<Source, Target, TargetKey, Extra> | ||
} | ||
}[keyof Target] | ||
type Schema<Source, Target, Extra> = { | ||
to: keyof Target, | ||
from: keyof Source | Transmuter<Source, Target, Extra> | ||
} | ||
``` | ||
@@ -92,3 +92,3 @@ | ||
Map a property directly from source to target: | ||
Map a property directly from source to target when types are compatible: | ||
@@ -105,9 +105,9 @@ ```typescript | ||
const schema: Schema<Source, Target>[] = [ | ||
{ from: 'email', to: 'contactEmail' } | ||
]; | ||
{ to: 'contactEmail', from: 'email' } | ||
] | ||
``` | ||
#### 2. Custom Transmutation Functions | ||
#### 2. Custom Transmuter Functions | ||
Transmute properties using custom logic with type safety: | ||
Transmute properties using custom logic with full type safety: | ||
@@ -133,15 +133,16 @@ ```typescript | ||
Include additional context in transmutations: | ||
Include additional context in transmutations through the `extra` parameter: | ||
```typescript | ||
interface Source { | ||
price: number; | ||
city: string; | ||
country: string; | ||
} | ||
interface Target { | ||
formattedPrice: string; | ||
location: string; | ||
} | ||
interface ExtraData { | ||
currency: string; | ||
separator: string; | ||
} | ||
@@ -151,93 +152,61 @@ | ||
{ | ||
to: 'formattedPrice', | ||
to: 'location', | ||
from: ({ source, extra }) => | ||
`${source.price.toFixed(2)} ${extra.currency}` | ||
`${source.city}${extra.separator}${source.country}` | ||
} | ||
]; | ||
const result = transmute(schema, | ||
{ city: 'New York', country: 'USA' }, | ||
{ separator: ', ' } | ||
); | ||
// Result: { location: 'New York, USA' } | ||
``` | ||
### Handling Undefined Values | ||
## API Reference | ||
When a source property doesn't exist or a transmutation function returns undefined, the target property will remain undefined: | ||
### Types | ||
```typescript | ||
interface Source { | ||
existingField: string; | ||
} | ||
// Arguments passed to a mutation function | ||
type TransmuterArgs<Source, Extra> = { source: Source, extra?: Extra } | ||
interface Target { | ||
mappedField: string; | ||
computedField: string; | ||
// Function that performs a custom transmutation | ||
type Transmuter<Source, Target, Extra> = (args: TransmuterArgs<Source, Extra>) => Target[keyof Target] | ||
// Defines how a property should be transmuted | ||
type Schema<Source, Target, Extra> = { | ||
to: keyof Target, | ||
from: keyof Source | Transmuter<Source, Target, Extra> | ||
} | ||
const schema: Schema<Source, Target>[] = [ | ||
{ | ||
from: 'nonExistentField' as keyof Source, // Property doesn't exist | ||
to: 'mappedField' | ||
}, | ||
{ | ||
to: 'computedField', | ||
from: ({ source }) => undefined // Transmutation returns undefined | ||
} | ||
]; | ||
const result = transmute(schema, { existingField: 'value' }); | ||
// Result: { mappedField: undefined, computedField: undefined } | ||
``` | ||
This allows you to: | ||
- Distinguish between unset values (`undefined`) and explicitly set null values | ||
- Handle optional properties naturally | ||
- Process partial transmutations as needed | ||
### transmute() | ||
## API Reference | ||
Main function for performing object transmutations. | ||
### `transmute<Source, Target, Extra = unknown>` | ||
```typescript | ||
function transmute<Source, Target, Extra>( | ||
schema: Schema<Source, Target, Extra>[], | ||
source: Source, | ||
extra?: Extra | ||
): Target; | ||
``` | ||
Main transmutation function. | ||
#### Parameters | ||
| Parameter | Type | Description | | ||
|-----------|-----------------------------------|------------------------------| | ||
| schema | `Schema<Source, Target, Extra>[]` | Array of transmutation rules | | ||
| source | `Source` | Source object to transmut | | ||
| extra? | `Extra` | Optional additional data | | ||
| Parameter | Type | Description | | ||
|-----------|-----------------------------------|------------------------------------| | ||
| schema | `Schema<Source, Target, Extra>[]` | Array of transmutation rules | | ||
| source | `Source` | Source object to transmute | | ||
| extra | `Extra` (optional) | Additional data for transmutations | | ||
#### Returns | ||
Returns an object of type `Target`. | ||
Returns an object of type `Target` with all specified transmutations applied. | ||
### Type Definitions | ||
### Type Safety Examples | ||
```typescript | ||
/** | ||
* Schema entry defining how a property should be transmuted | ||
*/ | ||
type Schema<Source, Target, Extra = unknown> = { | ||
[TargetKey in keyof Target]: { | ||
/** Target property key */ | ||
to: TargetKey | ||
/** Source property key for direct mapping or a custom transmutation function */ | ||
from: keyof Source | TransmuteFn<Source, Target, TargetKey, Extra> | ||
} | ||
}[keyof Target] | ||
The library provides strong type checking for both direct mappings and custom transmutations: | ||
/** | ||
* Function that performs property transmutation | ||
*/ | ||
type TransmuteFn<Source, Target, TargetKey extends keyof Target, Extra = unknown> = | ||
(args: TransmuteFnArgs<Source, Extra>) => Target[TargetKey] | ||
/** | ||
* Arguments passed to transmutation function | ||
*/ | ||
type TransmuteFnArgs<Source, Extra> = { | ||
source: Source | ||
extra?: Extra | ||
} | ||
``` | ||
### Type Safety Examples | ||
```typescript | ||
@@ -251,23 +220,23 @@ interface Source { | ||
interface Target { | ||
fullName: string; // TargetKey = 'fullName', type = string | ||
isAdult: boolean; // TargetKey = 'isAdult', type = boolean | ||
fullName: string; | ||
isAdult: boolean; | ||
} | ||
// TypeScript enforces correct return types | ||
const schema: Schema<Source, Target>[] = [ | ||
// Correct usage - types match | ||
const validSchema: Schema<Source, Target>[] = [ | ||
{ | ||
to: 'fullName', | ||
from: ({ source }) => `${source.firstName} ${source.lastName}` // Must return string | ||
from: ({ source }) => `${source.firstName} ${source.lastName}` // Returns string | ||
}, | ||
{ | ||
to: 'isAdult', | ||
from: ({ source }) => source.age >= 18 // Must return boolean | ||
from: ({ source }) => source.age >= 18 // Returns boolean | ||
} | ||
]; | ||
// Type error example: | ||
// Type error - incorrect return type | ||
const invalidSchema: Schema<Source, Target>[] = [ | ||
{ | ||
to: 'isAdult', | ||
from: ({ source }) => source.age // Type error: number is not assignable to boolean | ||
from: ({ source }) => source.age // Error: number not assignable to boolean | ||
} | ||
@@ -277,50 +246,2 @@ ]; | ||
### Direct Property Mapping | ||
When using direct property mapping, TypeScript ensures type compatibility: | ||
```typescript | ||
interface Source { | ||
email: string; | ||
age: number; | ||
} | ||
interface Target { | ||
contactEmail: string; | ||
yearOfBirth: number; | ||
} | ||
const schema: Schema<Source, Target>[] = [ | ||
{ from: 'email', to: 'contactEmail' }, // OK: string -> string | ||
{ from: 'age', to: 'yearOfBirth' } // OK: number -> number | ||
]; | ||
``` | ||
### Using Extra Data | ||
Extra data is fully typed: | ||
```typescript | ||
interface ExtraData { | ||
currentYear: number; | ||
} | ||
interface Source { | ||
age: number; | ||
} | ||
interface Target { | ||
yearOfBirth: number; | ||
} | ||
const schema: Schema<Source, Target, ExtraData>[] = [ | ||
{ | ||
to: 'yearOfBirth', | ||
from: ({ source, extra }) => extra.currentYear - source.age | ||
} | ||
]; | ||
const result = transmute(schema, { age: 25 }, { currentYear: 2024 }); | ||
``` | ||
## Contributing | ||
@@ -327,0 +248,0 @@ |
98
11003
249