Skip to content

Commit e88ad59

Browse files
authored
Merge pull request #194 from nobrainr/fix/update-next-with-fixes-from-master
Fix: Update next with fixes from master
2 parents b4a4b5d + e9612f9 commit e88ad59

File tree

6 files changed

+1635
-1196
lines changed

6 files changed

+1635
-1196
lines changed

.github/PULL_REQUEST_TEMPLATE.MD

+2-8
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,9 @@
33
<!--- where <type> can be feat/fix/doc/style/refactor/perf/test/chore -->
44

55
## Description
6-
<!--- Describe your changes in detail -->
76

8-
## Checklist
9-
- [ ] No duplicated code
10-
- [ ] No temporary code (console.log, commented code, dead code)
11-
- [ ] Code self-documentated (code hints, unit test names)
12-
- [ ] No sensitive information logged / sent
13-
- [ ] Error management with proper message
14-
- [ ] Unit tests 100% (new code, edited code, exhaustive)
7+
<!--- Describe your changes in detail -->
158

169
## Related Issue
10+
1711
<!--- Closes #32 where #32 is github number issue-->

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "morphism",
33
"version": "1.0.0",
4-
"description": "Do not repeat anymore your objects transformations.",
4+
"description": "Type-safe object transformer for JavaScript, TypeScript & Node.js. ",
55
"homepage": "https://github.com/nobrainr/morphism",
66
"main": "./dist/morphism.js",
77
"types": "./dist/types/morphism.d.ts",

src/morphism.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,17 @@ function morphism<TSchema extends Schema, TDestination>(
175175
type: Constructable<TDestination>
176176
): Mapper<TSchema, TDestination>; // morphism({}, null, T) => mapper(S) => T
177177

178-
function morphism<TSchema extends Schema, Target>(schema: TSchema, items: SourceFromSchema<TSchema>, type: Constructable<Target>): Target; // morphism({}, {}, T) => T
178+
function morphism<
179+
TSchema = Schema<DestinationFromSchema<Schema>, SourceFromSchema<Schema>>,
180+
Target = never,
181+
Source extends SourceFromSchema<TSchema> = SourceFromSchema<TSchema>
182+
>(schema: TSchema, items: Source, type: Constructable<Target>): Target; // morphism({}, {}, T) => T
183+
184+
function morphism<
185+
TSchema = Schema<DestinationFromSchema<Schema>, SourceFromSchema<Schema>>,
186+
Target = never,
187+
Source extends SourceFromSchema<TSchema> = SourceFromSchema<TSchema>
188+
>(schema: TSchema, items: Source[], type: Constructable<Target>): Target[]; // morphism({}, [], T) => T[]
179189

180190
function morphism<Target, Source, TSchema extends Schema<Target, Source>>(
181191
schema: TSchema,

src/types.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,22 @@ export type StrictSchema<Target = any, Source = any> = {
3535
/** `destinationProperty` is the name of the property of the target object you want to produce */
3636
[destinationProperty in keyof Target]:
3737
| ActionString<Source>
38-
| ActionFunction<Target, Source, Target[destinationProperty]>
38+
| {
39+
(iteratee: Source, source: Source[], target: Target[destinationProperty]): Target[destinationProperty];
40+
}
3941
| ActionAggregator<Source>
40-
| ActionSelector<Source, Target>
42+
| ActionSelector<Source, Target, destinationProperty>
4143
| StrictSchema<Target[destinationProperty], Source>;
4244
} & { [SCHEMA_OPTIONS_SYMBOL]?: SchemaOptions<Target> };
4345
export type Schema<Target = any, Source = any> = {
4446
/** `destinationProperty` is the name of the property of the target object you want to produce */
4547
[destinationProperty in keyof Target]?:
4648
| ActionString<Source>
47-
| ActionFunction<Target, Source, Target[destinationProperty]>
49+
| {
50+
(iteratee: Source, source: Source[], target: Target[destinationProperty]): Target[destinationProperty];
51+
}
4852
| ActionAggregator<Source>
49-
| ActionSelector<Source, Target>
53+
| ActionSelector<Source, Target, destinationProperty>
5054
| Schema<Target[destinationProperty], Source>;
5155
} & { [SCHEMA_OPTIONS_SYMBOL]?: SchemaOptions<Target | any> };
5256

@@ -157,9 +161,9 @@ export type ActionAggregator<T extends unknown = unknown> = T extends object ? (
157161
*```
158162
*
159163
*/
160-
export interface ActionSelector<Source = object, Target = any> {
164+
export interface ActionSelector<Source = object, Target = any, TargetProperty extends keyof Target = any> {
161165
path?: ActionString<Source> | ActionAggregator<Source>;
162-
fn?: (fieldValue: any, object: Source, items: Source, objectToCompute: Target) => Target;
166+
fn?: (fieldValue: any, object: Source, items: Source, objectToCompute: Target) => Target[TargetProperty];
163167
validation?: ValidateFunction;
164168
}
165169

src/typescript.spec.ts

+61-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Morphism, { morphism, StrictSchema, Schema } from './morphism';
1+
import Morphism, { morphism, StrictSchema, Schema, createSchema } from './morphism';
22

33
describe('Typescript', () => {
44
describe('Registry Type Checking', () => {
@@ -143,8 +143,12 @@ describe('Typescript', () => {
143143
d.namingIsHard;
144144

145145
morphism({ namingIsHard: 'boring_api_field' });
146-
morphism<StrictSchema<Destination, Source>>({ namingIsHard: 'boring_api_field' })({ boring_api_field: 2 });
147-
const e = morphism<StrictSchema<Destination>>({ namingIsHard: 'boring_api_field' })([{ boring_api_field: 2 }]);
146+
morphism<StrictSchema<Destination, Source>>({
147+
namingIsHard: 'boring_api_field',
148+
})({ boring_api_field: 2 });
149+
const e = morphism<StrictSchema<Destination>>({
150+
namingIsHard: 'boring_api_field',
151+
})([{ boring_api_field: 2 }]);
148152
const itemE = e.pop();
149153
expect(itemE).toBeDefined();
150154
if (itemE) {
@@ -161,6 +165,37 @@ describe('Typescript', () => {
161165
morphism<StrictSchema<D1, S1>>({ a: ({ _a }) => _a.toString() });
162166
morphism<StrictSchema<D1, S1>>({ a: ({ _a }) => _a.toString() });
163167
});
168+
169+
it('shoud infer result type from source when a class is provided', () => {
170+
class Source {
171+
constructor(public id: number, public ugly_field: string) {}
172+
}
173+
174+
class Destination {
175+
constructor(public id: number, public field: string) {}
176+
}
177+
178+
const source = [new Source(1, 'abc'), new Source(1, 'def')];
179+
180+
const schema: StrictSchema<Destination, Source> = {
181+
id: 'id',
182+
field: 'ugly_field',
183+
};
184+
const expected = [new Destination(1, 'abc'), new Destination(1, 'def')];
185+
186+
const result = morphism(schema, source, Destination);
187+
result.forEach((item, idx) => {
188+
expect(item).toEqual(expected[idx]);
189+
});
190+
});
191+
192+
it('should accept union types as Target', () => {
193+
const schema = createSchema<{ a: string } | { a: string; b: string }, { c: string }>({
194+
a: ({ c }) => c,
195+
});
196+
197+
expect(morphism(schema, { c: 'result' }).a).toEqual('result');
198+
});
164199
});
165200

166201
describe('Morphism Function Type Checking', () => {
@@ -180,4 +215,27 @@ describe('Typescript', () => {
180215
expect(morphism(schema, rows)[0].id).toEqual(1234);
181216
});
182217
});
218+
219+
describe('Selector Action', () => {
220+
it('should match return type of fn with target property', () => {
221+
interface Source {
222+
foo: string;
223+
}
224+
225+
interface Target {
226+
foo: number;
227+
}
228+
229+
const schema: StrictSchema<Target, Source> = {
230+
foo: {
231+
path: 'foo',
232+
fn: val => {
233+
return Number(val);
234+
},
235+
},
236+
};
237+
const source: Source = { foo: '1' };
238+
expect(morphism(schema, source)).toEqual({ foo: 1 });
239+
});
240+
});
183241
});

0 commit comments

Comments
 (0)