Skip to content

Commit

Permalink
Merge pull request #194 from nobrainr/fix/update-next-with-fixes-from…
Browse files Browse the repository at this point in the history
…-master

Fix: Update next with fixes from master
  • Loading branch information
emyann authored May 3, 2020
2 parents b4a4b5d + e9612f9 commit e88ad59
Show file tree
Hide file tree
Showing 6 changed files with 1,635 additions and 1,196 deletions.
10 changes: 2 additions & 8 deletions .github/PULL_REQUEST_TEMPLATE.MD
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@
<!--- where <type> can be feat/fix/doc/style/refactor/perf/test/chore -->

## Description
<!--- Describe your changes in detail -->

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

## Related Issue

<!--- Closes #32 where #32 is github number issue-->
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "morphism",
"version": "1.0.0",
"description": "Do not repeat anymore your objects transformations.",
"description": "Type-safe object transformer for JavaScript, TypeScript & Node.js. ",
"homepage": "https://github.com/nobrainr/morphism",
"main": "./dist/morphism.js",
"types": "./dist/types/morphism.d.ts",
Expand Down
12 changes: 11 additions & 1 deletion src/morphism.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,17 @@ function morphism<TSchema extends Schema, TDestination>(
type: Constructable<TDestination>
): Mapper<TSchema, TDestination>; // morphism({}, null, T) => mapper(S) => T

function morphism<TSchema extends Schema, Target>(schema: TSchema, items: SourceFromSchema<TSchema>, type: Constructable<Target>): Target; // morphism({}, {}, T) => T
function morphism<
TSchema = Schema<DestinationFromSchema<Schema>, SourceFromSchema<Schema>>,
Target = never,
Source extends SourceFromSchema<TSchema> = SourceFromSchema<TSchema>
>(schema: TSchema, items: Source, type: Constructable<Target>): Target; // morphism({}, {}, T) => T

function morphism<
TSchema = Schema<DestinationFromSchema<Schema>, SourceFromSchema<Schema>>,
Target = never,
Source extends SourceFromSchema<TSchema> = SourceFromSchema<TSchema>
>(schema: TSchema, items: Source[], type: Constructable<Target>): Target[]; // morphism({}, [], T) => T[]

function morphism<Target, Source, TSchema extends Schema<Target, Source>>(
schema: TSchema,
Expand Down
16 changes: 10 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,22 @@ export type StrictSchema<Target = any, Source = any> = {
/** `destinationProperty` is the name of the property of the target object you want to produce */
[destinationProperty in keyof Target]:
| ActionString<Source>
| ActionFunction<Target, Source, Target[destinationProperty]>
| {
(iteratee: Source, source: Source[], target: Target[destinationProperty]): Target[destinationProperty];
}
| ActionAggregator<Source>
| ActionSelector<Source, Target>
| ActionSelector<Source, Target, destinationProperty>
| StrictSchema<Target[destinationProperty], Source>;
} & { [SCHEMA_OPTIONS_SYMBOL]?: SchemaOptions<Target> };
export type Schema<Target = any, Source = any> = {
/** `destinationProperty` is the name of the property of the target object you want to produce */
[destinationProperty in keyof Target]?:
| ActionString<Source>
| ActionFunction<Target, Source, Target[destinationProperty]>
| {
(iteratee: Source, source: Source[], target: Target[destinationProperty]): Target[destinationProperty];
}
| ActionAggregator<Source>
| ActionSelector<Source, Target>
| ActionSelector<Source, Target, destinationProperty>
| Schema<Target[destinationProperty], Source>;
} & { [SCHEMA_OPTIONS_SYMBOL]?: SchemaOptions<Target | any> };

Expand Down Expand Up @@ -157,9 +161,9 @@ export type ActionAggregator<T extends unknown = unknown> = T extends object ? (
*```
*
*/
export interface ActionSelector<Source = object, Target = any> {
export interface ActionSelector<Source = object, Target = any, TargetProperty extends keyof Target = any> {
path?: ActionString<Source> | ActionAggregator<Source>;
fn?: (fieldValue: any, object: Source, items: Source, objectToCompute: Target) => Target;
fn?: (fieldValue: any, object: Source, items: Source, objectToCompute: Target) => Target[TargetProperty];
validation?: ValidateFunction;
}

Expand Down
64 changes: 61 additions & 3 deletions src/typescript.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Morphism, { morphism, StrictSchema, Schema } from './morphism';
import Morphism, { morphism, StrictSchema, Schema, createSchema } from './morphism';

describe('Typescript', () => {
describe('Registry Type Checking', () => {
Expand Down Expand Up @@ -143,8 +143,12 @@ describe('Typescript', () => {
d.namingIsHard;

morphism({ namingIsHard: 'boring_api_field' });
morphism<StrictSchema<Destination, Source>>({ namingIsHard: 'boring_api_field' })({ boring_api_field: 2 });
const e = morphism<StrictSchema<Destination>>({ namingIsHard: 'boring_api_field' })([{ boring_api_field: 2 }]);
morphism<StrictSchema<Destination, Source>>({
namingIsHard: 'boring_api_field',
})({ boring_api_field: 2 });
const e = morphism<StrictSchema<Destination>>({
namingIsHard: 'boring_api_field',
})([{ boring_api_field: 2 }]);
const itemE = e.pop();
expect(itemE).toBeDefined();
if (itemE) {
Expand All @@ -161,6 +165,37 @@ describe('Typescript', () => {
morphism<StrictSchema<D1, S1>>({ a: ({ _a }) => _a.toString() });
morphism<StrictSchema<D1, S1>>({ a: ({ _a }) => _a.toString() });
});

it('shoud infer result type from source when a class is provided', () => {
class Source {
constructor(public id: number, public ugly_field: string) {}
}

class Destination {
constructor(public id: number, public field: string) {}
}

const source = [new Source(1, 'abc'), new Source(1, 'def')];

const schema: StrictSchema<Destination, Source> = {
id: 'id',
field: 'ugly_field',
};
const expected = [new Destination(1, 'abc'), new Destination(1, 'def')];

const result = morphism(schema, source, Destination);
result.forEach((item, idx) => {
expect(item).toEqual(expected[idx]);
});
});

it('should accept union types as Target', () => {
const schema = createSchema<{ a: string } | { a: string; b: string }, { c: string }>({
a: ({ c }) => c,
});

expect(morphism(schema, { c: 'result' }).a).toEqual('result');
});
});

describe('Morphism Function Type Checking', () => {
Expand All @@ -180,4 +215,27 @@ describe('Typescript', () => {
expect(morphism(schema, rows)[0].id).toEqual(1234);
});
});

describe('Selector Action', () => {
it('should match return type of fn with target property', () => {
interface Source {
foo: string;
}

interface Target {
foo: number;
}

const schema: StrictSchema<Target, Source> = {
foo: {
path: 'foo',
fn: val => {
return Number(val);
},
},
};
const source: Source = { foo: '1' };
expect(morphism(schema, source)).toEqual({ foo: 1 });
});
});
});
Loading

0 comments on commit e88ad59

Please sign in to comment.