From 013bfd9bf4a256386b50ce47cacc2aaf316ba1aa Mon Sep 17 00:00:00 2001 From: Yang Liu Date: Thu, 12 Dec 2019 10:32:23 -0800 Subject: [PATCH] extend type use TS declaration merge feature --- CHANGELOG.md | 5 +++++ README.md | 44 +++++++------------------------------------- index.ts | 19 +++++++++++++++++++ test/sample.ts | 44 +++++++++++++++++++++++++------------------- 4 files changed, 56 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df8da5a..53d8ae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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` diff --git a/README.md b/README.md index 82ee7d2..7df8439 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ const iGuest = decoder.decode('IGuest', data); const guest = new Guest(iGuest); ``` -You may use the `extend` function to extend the interface: +You may subclass `Model` to extend the interface: ```typescript import { extend } from 'io-interface'; @@ -220,18 +220,17 @@ interface IUser { lastName: string; } -const User = extend()(user => ({ +interface User extends IUser {} +class User { get name(): string { return `${user.firstName} ${user.lastName}`; }, -})); - -type User = InstanceType; +} decoder.register({ schema: schema(), - constructor: User, // matches "const User" - className: 'User', // matches name of "type User" + constructor: User, + className: 'User', }); const user = decoder.decode('User', { firstName: 'Yang', lastName: 'Liu' }); @@ -239,35 +238,6 @@ const user = decoder.decode('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()(c => ({ - get boss(): string { - return c.user.name; - }, -})); -type Company = InstanceType; - -// register company - -const json = { - name: 'Good Inc', - user: { - firstName: 'Yang', - lastName: 'Liu', - }, -}; - -const company = decoder.decode('Company', json); -``` - ## Installation ### Setup ts-patch @@ -432,7 +402,7 @@ class ApiService { options: ApiOptions, cb: (c: Decoder, data: unknown, e?: DecoderCallback) => T | undefined, ) { - const data: Object = await fecth(options); + const data: Object = await fetch(options); const casted: T = cb(c, data, console.error); return casted; } diff --git a/index.ts b/index.ts index e8570de..f88cb94 100644 --- a/index.ts +++ b/index.ts @@ -120,6 +120,7 @@ export type Builder = ClassBuilder | CasterBuilder; /** * Extends type T with mixin U. + * @deprecated use Model instead * @since 1.6.0 * @example * interface IUser { @@ -146,6 +147,24 @@ export function extend() { }; } +/** + * Allow constructing from a plain object. + * @since 1.10.0 + * @example + * interface IUser { name: string; } + * interface User extends IUser {} + * class User extends Model { + * get title() { + * return `Dr. ${this.name}`; + * } + * } + */ +export class Model { + constructor(data: T) { + Object.assign(this, data); + } +} + function isClassBuilder(o: any): o is ClassBuilder { return ['schema', 'className', 'constructor'].every(k => k in o); } diff --git a/test/sample.ts b/test/sample.ts index 0afe1fe..d66af94 100644 --- a/test/sample.ts +++ b/test/sample.ts @@ -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'; @@ -372,13 +372,19 @@ interface ICustomer { lastName: string; } -const Customer = extend()(cus => ({ - get fullName(): string { - return `${cus.firstName} ${cus.lastName}`; - }, -})); +// const Customer = extend()(cus => ({ +// get fullName(): string { +// return `${cus.firstName} ${cus.lastName}`; +// }, +// })); -type Customer = InstanceType; +// type Customer = InstanceType; +interface Customer extends ICustomer {} +class Customer extends Model { + get fullName(): string { + return `${this.firstName} ${this.lastName}`; + } +} dec.register({ schema: schema(), @@ -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!!!'); } @@ -407,13 +414,12 @@ interface ICompany { customer: Customer; } -const Company = extend()(c => ({ +interface Company extends ICompany {} +class Company extends Model { get boss(): string { - return c.name; - }, -})); - -type Company = InstanceType; + return this.name; + } +} dec.register({ schema: schema(), @@ -433,7 +439,7 @@ const company: Company | undefined = dec.decode('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!!!'); } @@ -537,12 +543,12 @@ test('enumSchema error example', 'OrderStatus', st2, false); interface IDay { date: Date | null; } -const Day = extend()(d => ({ +interface Day extends IDay {} +class Day extends Model { get d() { - return d.date; - }, -})); -type Day = InstanceType; + return this.date; + } +} dec.register({ schema: schema(),