@@ -16,21 +16,56 @@ export function GetMethodDescriptor(propertyName: ts.PropertyName, methodSignatu
1616 const propertyNameString : string = TypescriptHelper . GetStringPropertyName ( propertyName ) ;
1717 const propertyNameStringLiteral : ts . StringLiteral = ts . createStringLiteral ( propertyNameString ) ;
1818
19- const [ signatureWithMostParameters ] : MethodSignature [ ] = [ ...methodSignatures ] . sort (
20- (
21- { parameters : leftParameters = [ ] } : MethodSignature ,
22- { parameters : rightParameters = [ ] } : MethodSignature ,
23- ) => rightParameters . length - leftParameters . length ,
19+ const signatureWithMostParameters : MethodSignature = methodSignatures . reduce (
20+ ( acc : MethodSignature , signature : MethodSignature ) => {
21+ const longestParametersLength : number = ( acc . parameters || [ ] ) . length ;
22+ const parametersLength : number = ( signature . parameters || [ ] ) . length ;
23+
24+ return parametersLength < longestParametersLength ? acc : signature ;
25+ } ,
2426 ) ;
2527
2628 const longestParameterList : ts . ParameterDeclaration [ ] = signatureWithMostParameters . parameters || [ ] ;
2729
28- const block : ts . Block = ts . createBlock (
29- [
30- ResolveSignatureElseBranch ( methodSignatures , longestParameterList ) ,
31- ] ,
32- true ,
33- ) ;
30+ const declarationVariableMap : Map < ts . ParameterDeclaration , ts . Identifier > = new Map < ts . ParameterDeclaration , ts . Identifier > ( ) ;
31+
32+ let i : number = 0 ;
33+ const declarationVariables : ts . VariableDeclaration [ ] = methodSignatures . reduce (
34+ ( variables : ts . VariableDeclaration [ ] , { parameters = [ ] } : MethodSignature ) => {
35+ for ( const parameter of parameters ) {
36+ if ( declarationVariableMap . has ( parameter ) ) {
37+ continue ;
38+ }
39+
40+ const declarationType : ts . TypeNode | undefined = parameter . type ;
41+ if ( declarationType && ts . isTypeReferenceNode ( declarationType ) ) {
42+ const variableIdentifier : ts . Identifier = ts . createIdentifier ( `__${ i ++ } ` ) ;
43+
44+ declarationVariableMap . set ( parameter , variableIdentifier ) ;
45+
46+ const declaration : ts . Declaration = TypescriptHelper . GetDeclarationFromNode ( declarationType . typeName ) ;
47+
48+ variables . push (
49+ TypescriptCreator . createVariableDeclaration (
50+ variableIdentifier ,
51+ ts . createStringLiteral ( MockDefiner . instance . getDeclarationKeyMap ( declaration ) ) ,
52+ ) ,
53+ ) ;
54+ }
55+ }
56+
57+ return variables ;
58+ } , [ ] as ts . VariableDeclaration [ ] ) ;
59+
60+ const statements : ts . Statement [ ] = [ ] ;
61+
62+ if ( declarationVariables . length ) {
63+ statements . push ( TypescriptCreator . createVariableStatement ( declarationVariables ) ) ;
64+ }
65+
66+ statements . push ( ResolveSignatureElseBranch ( declarationVariableMap , methodSignatures , longestParameterList ) ) ;
67+
68+ const block : ts . Block = ts . createBlock ( statements , true ) ;
3469
3570 const propertyValueFunction : ts . ArrowFunction = TypescriptCreator . createArrowFunction (
3671 block ,
@@ -40,7 +75,7 @@ export function GetMethodDescriptor(propertyName: ts.PropertyName, methodSignatu
4075 return TypescriptCreator . createCall ( providerGetMethod , [ propertyNameStringLiteral , propertyValueFunction ] ) ;
4176}
4277
43- function CreateTypeEquality ( signatureType : ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration ) : ts . Expression {
78+ function CreateTypeEquality ( signatureType : ts . Identifier | ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration ) : ts . Expression {
4479 const identifier : ts . Identifier = ts . createIdentifier ( primaryDeclaration . name . getText ( ) ) ;
4580
4681 if ( ! signatureType ) {
@@ -58,24 +93,30 @@ function CreateTypeEquality(signatureType: ts.TypeNode | undefined, primaryDecla
5893 ts . createTypeOf ( identifier ) ,
5994 signatureType ? ts . createStringLiteral ( signatureType . getText ( ) ) : ts . createVoidZero ( ) ,
6095 ) ;
61- } else {
62- // FIXME: Support `instanceof Class`, falls back to Object for now. The fallback causes undefined behavior!
63- return ts . createBinary ( identifier , ts . SyntaxKind . InstanceOfKeyword , ts . createIdentifier ( 'Object' ) ) ;
6496 }
97+
98+ if ( ts . isIdentifier ( signatureType ) ) {
99+ return ts . createStrictEquality (
100+ ts . createPropertyAccess ( identifier , '__factory' ) ,
101+ signatureType ,
102+ ) ;
103+ }
104+
105+ return ts . createBinary ( identifier , ts . SyntaxKind . InstanceOfKeyword , ts . createIdentifier ( 'Object' ) ) ;
65106}
66107
67- function CreateUnionTypeOfEquality ( signatureType : ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration ) : ts . Expression {
68- const typeNodes : ts . TypeNode [ ] = [ ] ;
108+ function CreateUnionTypeOfEquality ( signatureType : ts . Identifier | ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration ) : ts . Expression {
109+ const typeNodesAndVariableReferences : Array < ts . TypeNode | ts . Identifier > = [ ] ;
69110
70111 if ( signatureType ) {
71- if ( ts . isUnionTypeNode ( signatureType ) ) {
72- typeNodes . push ( ...signatureType . types ) ;
112+ if ( ts . isTypeNode ( signatureType ) && ts . isUnionTypeNode ( signatureType ) ) {
113+ typeNodesAndVariableReferences . push ( ...signatureType . types ) ;
73114 } else {
74- typeNodes . push ( signatureType ) ;
115+ typeNodesAndVariableReferences . push ( signatureType ) ;
75116 }
76117 }
77118
78- const [ firstType , ...remainingTypes ] : ts . TypeNode [ ] = typeNodes ;
119+ const [ firstType , ...remainingTypes ] : Array < ts . TypeNode | ts . Identifier > = typeNodesAndVariableReferences ;
79120
80121 return remainingTypes . reduce (
81122 ( prevStatement : ts . Expression , typeNode : ts . TypeNode ) =>
@@ -87,22 +128,53 @@ function CreateUnionTypeOfEquality(signatureType: ts.TypeNode | undefined, prima
87128 ) ;
88129}
89130
90- function ResolveParameterBranch ( declarations : ts . ParameterDeclaration [ ] , allDeclarations : ts . ParameterDeclaration [ ] , returnValue : ts . Expression , elseBranch : ts . Statement ) : ts . Statement {
131+ function ResolveParameterBranch (
132+ declarationVariableMap : Map < ts . ParameterDeclaration , ts . Identifier > ,
133+ declarations : ts . ParameterDeclaration [ ] ,
134+ allDeclarations : ts . ParameterDeclaration [ ] ,
135+ returnValue : ts . Expression ,
136+ elseBranch : ts . Statement ,
137+ ) : ts . Statement {
91138 const [ firstDeclaration , ...remainingDeclarations ] : Array < ts . ParameterDeclaration | undefined > = declarations ;
92139
140+ const variableReferenceOrType : ( declaration : ts . ParameterDeclaration ) => ts . Identifier | ts . TypeNode | undefined =
141+ ( declaration : ts . ParameterDeclaration ) => {
142+ if ( declarationVariableMap . has ( declaration ) ) {
143+ return declarationVariableMap . get ( declaration ) ;
144+ } else {
145+ return declaration . type ;
146+ }
147+ } ;
148+
149+ // TODO: These conditions quickly grow in size, but it should be possible to
150+ // squeeze things together and optimize it with something like:
151+ //
152+ // const typeOf = function (left, right) { return typeof left === right; }
153+ // const evaluate = (function(left, right) { return this._ = this._ || typeOf(left, right); }).bind({})
154+ //
155+ // if (evaluate(firstArg, 'boolean') && evaluate(secondArg, 'number') && ...) {
156+ // ...
157+ // }
158+ //
159+ // `this._' acts as a cache, since the control flow may evaluate the same
160+ // conditions multiple times.
93161 const condition : ts . Expression = remainingDeclarations . reduce (
94162 ( prevStatement : ts . Expression , declaration : ts . ParameterDeclaration , index : number ) =>
95163 ts . createLogicalAnd (
96164 prevStatement ,
97- CreateUnionTypeOfEquality ( declaration . type , allDeclarations [ index + 1 ] ) ,
165+ CreateUnionTypeOfEquality ( variableReferenceOrType ( declaration ) , allDeclarations [ index + 1 ] ) ,
98166 ) ,
99- CreateUnionTypeOfEquality ( firstDeclaration ?. type , allDeclarations [ 0 ] ) ,
167+ CreateUnionTypeOfEquality ( variableReferenceOrType ( firstDeclaration ) , allDeclarations [ 0 ] ) ,
100168 ) ;
101169
102170 return ts . createIf ( condition , ts . createReturn ( returnValue ) , elseBranch ) ;
103171}
104172
105- export function ResolveSignatureElseBranch ( signatures : MethodSignature [ ] , longestParameterList : ts . ParameterDeclaration [ ] ) : ts . Statement {
173+ export function ResolveSignatureElseBranch (
174+ declarationVariableMap : Map < ts . ParameterDeclaration , ts . Identifier > ,
175+ signatures : MethodSignature [ ] ,
176+ longestParameterList : ts . ParameterDeclaration [ ] ,
177+ ) : ts . Statement {
106178 const transformOverloadsOption : TsAutoMockOverloadOptions = GetTsAutoMockOverloadOptions ( ) ;
107179
108180 const [ signature , ...remainingSignatures ] : MethodSignature [ ] = signatures . filter ( ( _ : unknown , notFirst : number ) => transformOverloadsOption || ! notFirst ) ;
@@ -112,10 +184,10 @@ export function ResolveSignatureElseBranch(signatures: MethodSignature[], longes
112184 return ts . createReturn ( signature . returnValue ) ;
113185 }
114186
115- const elseBranch : ts . Statement = ResolveSignatureElseBranch ( remainingSignatures , longestParameterList ) ;
187+ const elseBranch : ts . Statement = ResolveSignatureElseBranch ( declarationVariableMap , remainingSignatures , longestParameterList ) ;
116188
117189 const currentParameters : ts . ParameterDeclaration [ ] = signature . parameters || [ ] ;
118- return ResolveParameterBranch ( currentParameters , longestParameterList , signature . returnValue , elseBranch ) ;
190+ return ResolveParameterBranch ( declarationVariableMap , currentParameters , longestParameterList , signature . returnValue , elseBranch ) ;
119191}
120192
121193function CreateProviderGetMethod ( ) : ts . PropertyAccessExpression {
0 commit comments