Browse Source

probably a working typecheck for callbacks

master
nitowa 2 years ago
parent
commit
a1f4055194
9 changed files with 257 additions and 63 deletions
  1. 2
    1
      Index.ts
  2. 3
    9
      src/Backend.ts
  3. 74
    0
      src/Decorator.ts
  4. 21
    12
      src/Frontend.ts
  5. 3
    1
      src/Strings.ts
  6. 62
    29
      src/Types.ts
  7. 8
    5
      src/Utils.ts
  8. 83
    5
      test/Test.ts
  9. 1
    1
      tsconfig.json

+ 2
- 1
Index.ts View File

@@ -2,4 +2,5 @@ export * from './src/Backend';
2 2
 export * from './src/Frontend';
3 3
 export * from './src/Interfaces'
4 4
 export * from './src/Types';
5
-export * from './src/Utils'
5
+export * from './src/Utils'
6
+export * from './src/Decorator'

+ 3
- 9
src/Backend.ts View File

@@ -5,7 +5,7 @@ import { PromiseIO } from "./PromiseIO/Server";
5 5
 import * as T from './Types';
6 6
 import * as U from './Utils';
7 7
 import * as I from './Interfaces';
8
-import { BAD_CONFIG_PARAM, UNKNOWN_RPC_IDENTIFIER, UNKNOWN_RPC_SERVER } from './Strings';
8
+import { BAD_CONFIG_PARAM, DESTROY_PREFIX, RPC_NO_NAME, UNKNOWN_RPC_IDENTIFIER, UNKNOWN_RPC_SERVER } from './Strings';
9 9
 
10 10
 export class RPCServer<
11 11
     InterfaceT extends T.RPCInterface = T.RPCInterface,
@@ -65,13 +65,7 @@ export class RPCServer<
65 65
 
66 66
         let badRPC = exporters.flatMap(ex => typeof ex.RPCs === "function" ? ex.RPCs() : (ex as any)).find(rpc => !rpc.name)
67 67
         if (badRPC) {
68
-            throw new Error(`
69
-            RPC did not provide a name. 
70
-            \nUse 'funtion name(..){ .. }' syntax instead.
71
-            \n
72
-            \n<------------OFFENDING RPC:
73
-            \n`+ badRPC.toString() + `
74
-            \n>------------OFFENDING RPC`)
68
+            throw new Error(RPC_NO_NAME(badRPC.toString()))
75 69
         }
76 70
 
77 71
         try {
@@ -114,7 +108,7 @@ export class RPCServer<
114 108
             if (this.conf.throwOnUnknownRPC) {
115 109
                 clientSocket.on("*", (packet) => {
116 110
                     if (!infos.some(i => i.uniqueName === packet.data[0])) {
117
-                        if (packet.data[0].startsWith('destroy_')) return
111
+                        if (packet.data[0].startsWith(DESTROY_PREFIX)) return
118 112
                         this.errorHandler(clientSocket, new Error(UNKNOWN_RPC_SERVER(packet.data[0])), packet.data[0], [...packet.data].splice(1), true)
119 113
                     }
120 114
                 })

+ 74
- 0
src/Decorator.ts View File

@@ -0,0 +1,74 @@
1
+import { CLASSNAME_ATTRIBUTE } from "./Strings"
2
+
3
+export abstract class DeserializerFactory {
4
+    static entityClasses = {}
5
+
6
+    static from(object: any) {
7
+        if(!object){
8
+            return
9
+        }
10
+
11
+        if(typeof object !== 'object'){ //definitely not a class object
12
+            return object
13
+        }
14
+        
15
+        if(Array.isArray(object)){
16
+            return object.map(DeserializerFactory.from)
17
+        }
18
+
19
+        const clazz = DeserializerFactory.entityClasses[object[CLASSNAME_ATTRIBUTE]]
20
+        delete object[CLASSNAME_ATTRIBUTE]
21
+
22
+        if(!clazz){ //anonymous object or class not registered as @Serializable
23
+            Object.keys(object).forEach(key => {
24
+                object[key] = DeserializerFactory.from(object[key])
25
+            })
26
+            return object
27
+        }
28
+
29
+        const obj = new clazz()
30
+        Object.keys(object).forEach(key => {
31
+            obj[key] = DeserializerFactory.from(object[key])
32
+        })
33
+        return obj
34
+    }
35
+
36
+    
37
+
38
+    static makeDeserializable(object: any){
39
+        if(!object)
40
+            return
41
+        
42
+        if(typeof object !== 'object')
43
+            return object
44
+        
45
+        if(object.constructor.name === 'Object'){
46
+            Object.keys(object).forEach(key => {
47
+                object[key] = DeserializerFactory.makeDeserializable(object[key])
48
+            })
49
+            return object
50
+        }
51
+
52
+        object[CLASSNAME_ATTRIBUTE] = object.constructor.name
53
+        Object.keys(object).forEach(key => {
54
+            object[key] = DeserializerFactory.makeDeserializable(object[key])
55
+        })
56
+
57
+        return object
58
+    }
59
+}
60
+
61
+export function Serializable(attr?: any) {
62
+    return function _Serializable<T extends { new(...args: any[]): {} }>(clazz: T) {
63
+        DeserializerFactory.entityClasses[clazz.name] = clazz
64
+        
65
+        return clazz
66
+        /*
67
+        return class extends clazz{
68
+            constructor(...args: any[]) {
69
+                super(...args)
70
+            }
71
+        }
72
+        */
73
+    }
74
+}

+ 21
- 12
src/Frontend.ts View File

@@ -4,7 +4,9 @@ import { PromiseIOClient, defaultClientConfig } from './PromiseIO/Client'
4 4
 import * as T from './Types';
5 5
 import * as I from './Interfaces';
6 6
 import { stripAfterEquals, appendComma } from './Utils';
7
-import { SOCKET_NOT_CONNECTED, UNKNOWN_RPC_IDENTIFIER, USER_DEFINED_TIMEOUT } from './Strings';
7
+import { DESTROY_PREFIX, SOCKET_NOT_CONNECTED, UNKNOWN_RPC_IDENTIFIER, USER_DEFINED_TIMEOUT } from './Strings';
8
+import { DeserializerFactory } from './Decorator';
9
+DeserializerFactory
8 10
 
9 11
 
10 12
 /**
@@ -117,17 +119,21 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
117 119
     public async call(rpcname: string, ...args: any[]): Promise<any> {
118 120
         if (!this.socket) throw new Error(SOCKET_NOT_CONNECTED)
119 121
 
120
-
121 122
         try {
122 123
             if(!this.conf.callTimeoutMs || this.conf.callTimeoutMs <= 0)
123 124
                 return await this.socket.call.apply(this.socket, [rpcname, ...args])
124 125
             else 
125
-                return await Promise.race([
126
-                    this.socket.call.apply(this.socket, [rpcname, ...args]),
127
-                    new Promise((_, rej) => {
128
-                        setTimeout(_ => rej(USER_DEFINED_TIMEOUT(this.conf.callTimeoutMs)), this.conf.callTimeoutMs)
129
-                    }) 
130
-                ])
126
+                if(this.conf.callTimeoutMs){
127
+                    return await Promise.race([
128
+                        this.socket.call.apply(this.socket, [rpcname, ...args]),
129
+                        new Promise((_, rej) => {
130
+                            setTimeout(_ => rej(USER_DEFINED_TIMEOUT(this.conf.callTimeoutMs)), this.conf.callTimeoutMs)
131
+                        }) 
132
+                    ])
133
+                }else{
134
+                    return await this.socket.call.apply(this.socket, [rpcname, ...args])
135
+                }
136
+                
131 137
         } catch (e) {
132 138
             this.emit('error', e)
133 139
             throw e
@@ -203,9 +209,10 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
203 209
         const headerArgs = fnArgs.join(",")
204 210
         const argParams = fnArgs.map(stripAfterEquals).join(",")
205 211
         sesame = appendComma(sesame)
206
-
212
+        const deserializer = DeserializerFactory
207 213
         return eval(`async (${headerArgs}) => { 
208
-            return await this.call("${fnName}", ${sesame} ${argParams})
214
+            const returnvalue = await this.call("${fnName}", ${sesame} ${argParams})
215
+            return deserializer.from(returnvalue)
209 216
         }`)
210 217
     }
211 218
 
@@ -223,6 +230,8 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
223 230
         const argParams = fnArgs.map(stripAfterEquals).join(",")
224 231
         sesame = appendComma(sesame, true)
225 232
         headerArgs = fnArgs.length > 0 ? headerArgs + "," : headerArgs
233
+        const deserializer = DeserializerFactory
234
+        const destroy_prefix = DESTROY_PREFIX
226 235
 
227 236
         const frontendHookStr = `
228 237
         async (${headerArgs} $__callback__$) => {
@@ -231,12 +240,12 @@ export class RPCSocket<Ifc extends T.RPCInterface = T.RPCInterface> implements I
231 240
                 if(r){
232 241
                     if(r.uuid){
233 242
                         $__callback__$['destroy'] = () => {
234
-                            this.socket.fire('destroy_'+r.uuid)
243
+                            this.socket.fire(destroy_prefix+r.uuid)
235 244
                             this.socket.unhook(r.uuid) 
236 245
                         }
237 246
                         this.socket.hook(r.uuid, $__callback__$)
238 247
                     }
239
-                    return r.return
248
+                    return deserializer.from(r.return)
240 249
                 }else{
241 250
                     throw new Error("Empty response")
242 251
                 }

+ 3
- 1
src/Strings.ts View File

@@ -11,4 +11,6 @@ RPC did not provide a name.
11 11
 \n
12 12
 \n<------------OFFENDING RPC:
13 13
 \n${name}
14
-\n>------------OFFENDING RPC`
14
+\n>------------OFFENDING RPC`
15
+export const CLASSNAME_ATTRIBUTE = "$__CLASSNAME__$"
16
+export const DESTROY_PREFIX = "$__DESTROY__$_"

+ 62
- 29
src/Types.ts View File

@@ -5,20 +5,20 @@ import { PromiseIO } from "./PromiseIO/Server";
5 5
 export type PioBindListener = (...args: any) => void
6 6
 export type PioHookListener = AnyFunction
7 7
 
8
-export type AnyFunction = (...args:any) => any
8
+export type AnyFunction = (...args: any[]) => any
9 9
 export type HookFunction = AnyFunction
10
-export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame:string|undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean> | boolean
10
+export type AccessFilter<InterfaceT extends RPCInterface = RPCInterface> = (sesame: string | undefined, exporter: I.RPCExporter<InterfaceT, keyof InterfaceT>) => Promise<boolean> | boolean
11 11
 export type Visibility = "127.0.0.1" | "0.0.0.0"
12
-export type ConnectionHandler = (socket:I.Socket) => void
13
-export type ErrorHandler = (socket:I.Socket, error:any, rpcName: string, args: any[]) => void
14
-export type CloseHandler = (socket:I.Socket) =>  void
15
-export type SesameFunction = (sesame : string) => boolean
12
+export type ConnectionHandler = (socket: I.Socket) => void
13
+export type ErrorHandler = (socket: I.Socket, error: any, rpcName: string, args: any[]) => void
14
+export type CloseHandler = (socket: I.Socket) => void
15
+export type SesameFunction = (sesame: string) => boolean
16 16
 export type SesameConf = {
17 17
     sesame?: string | SesameFunction
18 18
 }
19 19
 export type FrontEndHandlerType = {
20
-    'error' : (e: any) => void
21
-    'close' : () => void
20
+    'error': (e: any) => void
21
+    'close': () => void
22 22
 }
23 23
 export type ClientConfig = SocketIOClient.ConnectOpts & {
24 24
     protocol?: 'http' | 'https',
@@ -40,8 +40,8 @@ export type ResponseType = "Subscribe" | "Success" | "Error"
40 40
 export type Outcome = "Success" | "Error"
41 41
 
42 42
 export type Respose<T> = T & { result: Outcome }
43
-export type SuccessResponse<T = {}> = Respose<T> & { result: "Success" } 
44
-export type ErrorResponse<T = {}> = Respose<T> & { result: "Error", message?:string }
43
+export type SuccessResponse<T = {}> = Respose<T> & { result: "Success" }
44
+export type ErrorResponse<T = {}> = Respose<T> & { result: "Error", message?: string }
45 45
 
46 46
 export type RPCType = 'Hook' | 'Unhook' | 'Call'
47 47
 
@@ -53,17 +53,17 @@ export type CallRPC<Name, Func extends AnyFunction> = {
53 53
 
54 54
 export type HookRPC<Name, Func extends AnyFunction> = {
55 55
     name: Name
56
-    hook: Func
56
+    hook: AnyFunction
57 57
     onCallback?: AnyFunction
58 58
     onDestroy?: HookCloseFunction<ReturnType<Func> extends Promise<infer T> ? T : ReturnType<Func>>
59 59
 }
60 60
 
61
-export type RPC<Name, Func extends AnyFunction> = HookRPC<Name, Func> | CallRPC<Name,Func> | Func
61
+export type RPC<Name, Func extends AnyFunction> = HookRPC<Name, Func> | CallRPC<Name, Func> | Func
62 62
 
63 63
 export type RPCInterface<Impl extends RPCInterface = {}> = {
64
-    [grp in string] : {
65
-        [rpc in string] : AnyFunction
66
-    } 
64
+    [grp in string]: {
65
+        [rpc in string]: AnyFunction
66
+    }
67 67
 } & Impl
68 68
 
69 69
 export type exportT = {
@@ -71,7 +71,9 @@ export type exportT = {
71 71
 }
72 72
 
73 73
 export type RPCDefinitions<Ifc extends RPCInterface> = {
74
-    [grp in keyof Ifc]:( { [rpc in keyof Ifc[grp]]: RPC<rpc, Ifc[grp][rpc]> }[keyof Ifc[grp]] )[]
74
+    [grp in keyof Ifc]: ({ 
75
+        [rpc in keyof Ifc[grp]]: RPC<rpc, Ifc[grp][rpc]> 
76
+    }[keyof Ifc[grp]])[]
75 77
 }
76 78
 
77 79
 export type BaseInfo = {
@@ -80,9 +82,9 @@ export type BaseInfo = {
80 82
     argNames: string[],
81 83
 }
82 84
 
83
-export type HookInfo<SubresT = {}> = BaseInfo & { 
84
-    type: 'Hook', 
85
-    generator: (socket?:I.Socket) => (...args:any[]) => SubresT
85
+export type HookInfo<SubresT = {}> = BaseInfo & {
86
+    type: 'Hook',
87
+    generator: (socket?: I.Socket) => (...args: any[]) => SubresT
86 88
 }
87 89
 
88 90
 export type CallInfo = BaseInfo & {
@@ -90,15 +92,46 @@ export type CallInfo = BaseInfo & {
90 92
     call: AnyFunction
91 93
 }
92 94
 
93
-export type RpcInfo = HookInfo |  CallInfo
94
-export type ExtendedRpcInfo = RpcInfo & { uniqueName: string } 
95
+export type RpcInfo = HookInfo | CallInfo
96
+export type ExtendedRpcInfo = RpcInfo & { uniqueName: string }
95 97
 
96 98
 export type OnFunction = <T extends "error" | "close">(type: T, f: FrontEndHandlerType[T]) => void
97
-export type HookCloseFunction<T> = (res: T, rpc:HookRPC<any, any>) => any
98
-
99
-
100
-export type AsyncIfc<Ifc extends RPCInterface> = { [grp in keyof Ifc]: {[rpcname in keyof Ifc[grp]] : AsyncAnyFunction<Ifc[grp][rpcname]> } }
101
-
102
-export type AsyncAnyFunction<F extends AnyFunction = AnyFunction> = F extends (...args: Parameters<F>) => infer R 
103
-                                                                    ? ((...args: Parameters<F>) => R extends Promise<any> ? R : Promise<R> ) 
104
-                                                                    : Promise<any>
99
+export type HookCloseFunction<T> = (res: T, rpc: HookRPC<any, any>) => any
100
+
101
+
102
+export type AsyncIfc<Ifc extends RPCInterface> = { [grp in keyof Ifc]: { [rpcname in keyof Ifc[grp]]: AsyncAnyFunction<Ifc[grp][rpcname]> } }
103
+
104
+export type AsyncAnyFunction<F extends AnyFunction = AnyFunction> = F extends (...args: Parameters<F>) => infer R
105
+    ? ((...args: Parameters<F>) => R extends Promise<any> ? R : Promise<R>)
106
+    : Promise<any>
107
+
108
+type DYN_PARAM<
109
+    A = void, 
110
+    B = void,
111
+    C = void,
112
+    D = void,
113
+    E = void,
114
+    F = void,
115
+    G = void,
116
+    H = void,
117
+> = H extends void ?
118
+     G extends void ?
119
+      F extends void ?
120
+       E extends void ?
121
+        D extends void ?
122
+         C extends void ?
123
+          B extends void ?
124
+           A extends void ?
125
+            []
126
+            : [A]
127
+          : [A,B]
128
+         :[A,B,C]
129
+        :[A,B,C,D]
130
+       :[A,B,C,D,E]
131
+      :[A,B,C,D,E,F]
132
+     :[A,B,C,D,E,F,G]
133
+    :[A,B,C,D,E,F,G,H] 
134
+
135
+type Destroyable = { destroy: () => void }
136
+export type Callback<A0 = void, A1 = void, A2 = void, A3 = void, A4 = void, A5 = void, A6 = void, A7 = void> = 
137
+    (this: Destroyable, ...args: DYN_PARAM<A0, A1, A2, A3, A4, A5, A6, A7>) => void 

+ 8
- 5
src/Utils.ts View File

@@ -3,7 +3,8 @@ import * as uuidv4 from "uuid/v4"
3 3
 import * as T from "./Types";
4 4
 import * as I from "./Interfaces";
5 5
 import { Socket } from "socket.io"
6
-import { CALL_NOT_FOUND, RPC_BAD_TYPE, RPC_NO_NAME } from "./Strings";
6
+import { CALL_NOT_FOUND, DESTROY_PREFIX, RPC_BAD_TYPE, RPC_NO_NAME } from "./Strings";
7
+import { DeserializerFactory } from "./Decorator";
7 8
 
8 9
 /**
9 10
  * Translate an RPC to RPCInfo for serialization.
@@ -79,10 +80,12 @@ const callGenerator = (rpcName: string, $__socket__$: I.Socket, rpcFunction: T.A
79 80
     const argsArr = extractArgs(rpcFunction)
80 81
     const args = argsArr.join(',')
81 82
     const argsStr = argsArr.map(stripAfterEquals).join(',')
83
+    const deserializer = DeserializerFactory 
82 84
 
83 85
     const callStr = `async (${args}) => {
84 86
         try{
85
-            return await rpcFunction(${argsStr})
87
+            const res = await rpcFunction(${argsStr})
88
+            return deserializer.makeDeserializable(res)
86 89
         }catch(e){
87 90
             errorHandler($__socket__$, e, rpcName, [${args}])
88 91
         }
@@ -113,7 +116,7 @@ const hookGenerator = (rpc: T.HookRPC<any, any>, errorHandler: T.ErrorHandler, s
113 116
         : callArgs
114 117
 
115 118
     callArgs = appendComma(callArgs, false)
116
-
119
+    const destroy_prefix = DESTROY_PREFIX
117 120
     const hookStr = `
118 121
     ($__socket__$) => async (${args}) => {
119 122
         try{
@@ -123,7 +126,7 @@ const hookGenerator = (rpc: T.HookRPC<any, any>, errorHandler: T.ErrorHandler, s
123 126
                 ${rpc.onCallback ? `rpc.onCallback.apply({}, cbargs)` : ``}
124 127
                 $__socket__$.call.apply($__socket__$, [uuid, ...cbargs])
125 128
             })
126
-            ${rpc.onDestroy ? `$__socket__$.bind('destroy_'+uuid, () => {
129
+            ${rpc.onDestroy ? `$__socket__$.bind(destroy_prefix+uuid, () => {
127 130
                 rpc.onDestroy(res, rpc)
128 131
             })` : ``}
129 132
             return {'uuid': uuid, 'return': res}
@@ -144,7 +147,7 @@ const makeError = (callName: string) => new Error(CALL_NOT_FOUND(callName))
144 147
  */
145 148
 const extractArgs = (f: Function): string[] => {
146 149
     let fn: string
147
-    fn = (fn = String(f)).substr(0, fn.indexOf(")")).substr(fn.indexOf("(") + 1)
150
+    fn = (fn = String(f)).substring(0, fn.indexOf(")")).substring(fn.indexOf("(") + 1)
148 151
     return fn !== "" ? fn.split(',') : []
149 152
 }
150 153
 

+ 83
- 5
test/Test.ts View File

@@ -1,7 +1,7 @@
1 1
 import { describe, it } from "mocha";
2
-import { RPCServer, RPCSocket } from '../Index'
2
+import { RPCServer, RPCSocket, Serializable } from '../Index'
3 3
 import { RPCExporter, Socket } from "../src/Interfaces";
4
-import { ConnectedSocket } from "../src/Types";
4
+import { ConnectedSocket, Callback } from "../src/Types";
5 5
 import * as log from 'why-is-node-running';
6 6
 import * as http from 'http';
7 7
 import * as express from 'express';
@@ -614,7 +614,7 @@ type topicDTO = { topic: string; }
614 614
 type SesameTestIfc = {
615 615
     test: {
616 616
         checkCandy: () => Promise<string>
617
-        subscribe: (callback: Function) => Promise<topicDTO>
617
+        subscribe: (callback: Callback<string>) => Promise<topicDTO>
618 618
         manyParams: <A = string, B = number, C = boolean, D = Object>(a: A, b: B, c: C, d: D) => Promise<[A, B, C, D]>
619 619
     }
620 620
 }
@@ -688,7 +688,7 @@ describe('Sesame should unlock the socket', () => {
688 688
     })
689 689
 
690 690
     it('callback should work with sesame', (done) => {
691
-        client.test.subscribe((c) => {
691
+        client.test.subscribe(function(c){
692 692
             if (c === candy) {
693 693
                 done()
694 694
             }
@@ -1009,5 +1009,83 @@ describe("attaching handlers before connecting", () => {
1009 1009
             done(e)
1010 1010
         })
1011 1011
     })
1012
-
1013 1012
 })
1013
+
1014
+describe("class (de-)serialization", () => {
1015
+
1016
+    @Serializable()
1017
+    class SubClass{
1018
+        fString = "F"
1019
+    }
1020
+
1021
+    @Serializable()
1022
+    class TestClass{
1023
+        aString = "A"
1024
+        aNumber = 46
1025
+        aObject = {
1026
+            x: "x",
1027
+            y: undefined,
1028
+            sub: new SubClass()
1029
+        }
1030
+        aClassObject = new SubClass()
1031
+
1032
+        public returnOK(){
1033
+            return "OK"
1034
+        }
1035
+    }
1036
+
1037
+    let myServer: RPCServer;
1038
+    let mySocket: RPCSocket;
1039
+
1040
+    before(function(done){
1041
+        myServer = new RPCServer([{
1042
+            name: "Test",
1043
+            RPCs: [
1044
+                function returnClass(){
1045
+                    return new TestClass()
1046
+                }
1047
+            ]
1048
+        }])
1049
+        myServer.listen(8084)
1050
+
1051
+        mySocket = new RPCSocket(8084, 'localhost')
1052
+        mySocket.connect().then(() => done())
1053
+    })
1054
+
1055
+    after(function(done){
1056
+        mySocket.close()
1057
+        myServer.close()
1058
+        done()
1059
+    })
1060
+
1061
+
1062
+    it("receives class in call response", async () => {
1063
+        const obj: TestClass = await mySocket['Test'].returnClass()
1064
+
1065
+        expect(obj).to.be.an.instanceOf(TestClass)
1066
+        expect(obj.aString).to.be.a('string')
1067
+        expect(obj.aNumber).to.be.a('number')
1068
+        expect(obj.aObject).to.be.a('object')
1069
+        expect(obj.aObject.x).to.be.a('string')
1070
+        expect(obj.aObject.y).to.be.undefined
1071
+        expect(obj.aObject.sub).to.be.an.instanceOf(SubClass)
1072
+        expect(obj.aClassObject).to.be.an.instanceOf(SubClass)
1073
+
1074
+        expect(obj.returnOK()).to.be.equal('OK')
1075
+    })
1076
+
1077
+    it("receives class in hook response", async () => {
1078
+        const obj: TestClass = await mySocket['Test'].returnClass()
1079
+
1080
+        expect(obj).to.be.an.instanceOf(TestClass)
1081
+        expect(obj.aString).to.be.a('string')
1082
+        expect(obj.aNumber).to.be.a('number')
1083
+        expect(obj.aObject).to.be.a('object')
1084
+        expect(obj.aObject.x).to.be.a('string')
1085
+        expect(obj.aObject.y).to.be.undefined
1086
+        expect(obj.aObject.sub).to.be.an.instanceOf(SubClass)
1087
+        expect(obj.aClassObject).to.be.an.instanceOf(SubClass)
1088
+
1089
+        expect(obj.returnOK()).to.be.equal('OK')
1090
+    })
1091
+})

+ 1
- 1
tsconfig.json View File

@@ -9,6 +9,6 @@
9 9
       "strict": true,
10 10
       "experimentalDecorators": true
11 11
     },
12
-    "include": ["src/**/*.ts", "test/**/*.ts", "Index.ts", "demo.ts", "scratchpad.ts"],
12
+    "include": ["src/**/*.ts", "test/**/*.ts", "Index.ts", "demo.ts", "wat.ts"],
13 13
     "exclude": ["node_modules"]
14 14
   }

Loading…
Cancel
Save