Skip to content

Commit

Permalink
extend type use TS declaration merge feature
Browse files Browse the repository at this point in the history
  • Loading branch information
robturtle committed Dec 12, 2019
1 parent b468145 commit 013bfd9
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 56 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.10.0

- new way to extend type to a class: `Model`
- deprecates the old way `extend`

## 1.9.0

- supports `enumSchema`
Expand Down
44 changes: 7 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ const iGuest = decoder.decode<IGuest>('IGuest', data);
const guest = new Guest(iGuest);
```

You may use the `extend` function to extend the interface:
You may subclass `Model<T>` to extend the interface:

```typescript
import { extend } from 'io-interface';
Expand All @@ -220,54 +220,24 @@ interface IUser {
lastName: string;
}

const User = extend<IUser>()(user => ({
interface User extends IUser {}
class User {
get name(): string {
return `${user.firstName} ${user.lastName}`;
},
}));

type User = InstanceType<typeof User>;
}

decoder.register({
schema: schema<IUser>(),
constructor: User, // matches "const User"
className: 'User', // matches name of "type User"
constructor: User,
className: 'User',
});

const user = decoder.decode<User>('User', { firstName: 'Yang', lastName: 'Liu' });

console.log(user.name);
```

#### Extended constructors are composible

You can use a registered extended type as a field of other interface.

```typescript
interface ICompany {
user: User; // use a registed extended type here
name: string;
}
const Company = extend<ICompany>()(c => ({
get boss(): string {
return c.user.name;
},
}));
type Company = InstanceType<typeof Company>;

// register company

const json = {
name: 'Good Inc',
user: {
firstName: 'Yang',
lastName: 'Liu',
},
};

const company = decoder.decode<Company>('Company', json);
```

## Installation

### Setup ts-patch
Expand Down Expand Up @@ -432,7 +402,7 @@ class ApiService {
options: ApiOptions,
cb: (c: Decoder, data: unknown, e?: DecoderCallback<T>) => T | undefined,
) {
const data: Object = await fecth(options);
const data: Object = await fetch(options);
const casted: T = cb(c, data, console.error);
return casted;
}
Expand Down
19 changes: 19 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export type Builder = ClassBuilder | CasterBuilder;

/**
* Extends type T with mixin U.
* @deprecated use Model<T> instead
* @since 1.6.0
* @example
* interface IUser {
Expand All @@ -146,6 +147,24 @@ export function extend<T extends object>() {
};
}

/**
* Allow constructing from a plain object.
* @since 1.10.0
* @example
* interface IUser { name: string; }
* interface User extends IUser {}
* class User extends Model<IUser> {
* get title() {
* return `Dr. ${this.name}`;
* }
* }
*/
export class Model<T> {
constructor(data: T) {
Object.assign(this, data);
}
}

function isClassBuilder(o: any): o is ClassBuilder {
return ['schema', 'className', 'constructor'].every(k => k in o);
}
Expand Down
44 changes: 25 additions & 19 deletions test/sample.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Decoder, runtime, schema, extend, enumSchema } from '..';
import { Decoder, runtime, schema, extend, enumSchema, Model } from '..';
import { casters, Latitude, Longitude, Int, NonEmptyString } from '../types';
import * as t from 'io-ts';

Expand Down Expand Up @@ -372,13 +372,19 @@ interface ICustomer {
lastName: string;
}

const Customer = extend<ICustomer>()(cus => ({
get fullName(): string {
return `${cus.firstName} ${cus.lastName}`;
},
}));
// const Customer = extend<ICustomer>()(cus => ({
// get fullName(): string {
// return `${cus.firstName} ${cus.lastName}`;
// },
// }));

type Customer = InstanceType<typeof Customer>;
// type Customer = InstanceType<typeof Customer>;
interface Customer extends ICustomer {}
class Customer extends Model<ICustomer> {
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
}

dec.register({
schema: schema<ICustomer>(),
Expand All @@ -394,6 +400,7 @@ const cus: Customer | undefined = dec.decode('Customer', good15, e => {

if (cus) {
console.log('extend function:', JSON.stringify(cus, null, 2));
console.log('extend function: get fullName():', cus.fullName);
} else {
throw new Error('extend function NOT WORKING!!!');
}
Expand All @@ -407,13 +414,12 @@ interface ICompany {
customer: Customer;
}

const Company = extend<ICompany>()(c => ({
interface Company extends ICompany {}
class Company extends Model<ICompany> {
get boss(): string {
return c.name;
},
}));

type Company = InstanceType<typeof Company>;
return this.name;
}
}

dec.register({
schema: schema<ICompany>(),
Expand All @@ -433,7 +439,7 @@ const company: Company | undefined = dec.decode<Company>('Company', good37, e =>
throw e;
});
if (company) {
console.log('nested constructor passed', company);
console.log('nested constructor passed', JSON.stringify(company, null, 2));
} else {
throw new Error('nested constructor NOT WORKING!!!');
}
Expand Down Expand Up @@ -537,12 +543,12 @@ test('enumSchema error example', 'OrderStatus', st2, false);
interface IDay {
date: Date | null;
}
const Day = extend<IDay>()(d => ({
interface Day extends IDay {}
class Day extends Model<IDay> {
get d() {
return d.date;
},
}));
type Day = InstanceType<typeof Day>;
return this.date;
}
}

dec.register({
schema: schema<IDay>(),
Expand Down

0 comments on commit 013bfd9

Please sign in to comment.