@@ -4,7 +4,6 @@ import { TypescriptCreator } from '../../helper/creator';
44import { MockDefiner } from '../../mockDefiner/mockDefiner' ;
55import { ModuleName } from '../../mockDefiner/modules/moduleName' ;
66import { TypescriptHelper } from '../helper/helper' ;
7- import { TransformerLogger } from '../../logger/transformerLogger' ;
87
98export interface MethodSignature {
109 parameters ?: ts . ParameterDeclaration [ ] ;
@@ -17,21 +16,56 @@ export function GetMethodDescriptor(propertyName: ts.PropertyName, methodSignatu
1716 const propertyNameString : string = TypescriptHelper . GetStringPropertyName ( propertyName ) ;
1817 const propertyNameStringLiteral : ts . StringLiteral = ts . createStringLiteral ( propertyNameString ) ;
1918
20- const [ signatureWithMostParameters ] : MethodSignature [ ] = [ ...methodSignatures ] . sort (
21- (
22- { parameters : leftParameters = [ ] } : MethodSignature ,
23- { parameters : rightParameters = [ ] } : MethodSignature ,
24- ) => 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+ } ,
2526 ) ;
2627
2728 const longestParameterList : ts . ParameterDeclaration [ ] = signatureWithMostParameters . parameters || [ ] ;
2829
29- const block : ts . Block = ts . createBlock (
30- [
31- ResolveSignatureElseBranch ( methodSignatures , longestParameterList ) ,
32- ] ,
33- true ,
34- ) ;
30+ const declarationVariableMap : Map < ts . ParameterDeclaration , ts . Identifier > = new Map < ts . ParameterDeclaration , ts . Identifier > ( ) ;
31+
32+ const declarationVariables : ts . VariableDeclaration [ ] = methodSignatures . reduce (
33+ ( variables : ts . VariableDeclaration [ ] , { parameters = [ ] } : MethodSignature ) => {
34+ let i : number = 0 ;
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 ) ;
3569
3670 const propertyValueFunction : ts . ArrowFunction = TypescriptCreator . createArrowFunction (
3771 block ,
@@ -41,7 +75,7 @@ export function GetMethodDescriptor(propertyName: ts.PropertyName, methodSignatu
4175 return TypescriptCreator . createCall ( providerGetMethod , [ propertyNameStringLiteral , propertyValueFunction ] ) ;
4276}
4377
44- 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 {
4579 const identifier : ts . Identifier = ts . createIdentifier ( primaryDeclaration . name . getText ( ) ) ;
4680
4781 if ( ! signatureType ) {
@@ -59,25 +93,30 @@ function CreateTypeEquality(signatureType: ts.TypeNode | undefined, primaryDecla
5993 ts . createTypeOf ( identifier ) ,
6094 signatureType ? ts . createStringLiteral ( signatureType . getText ( ) ) : ts . createVoidZero ( ) ,
6195 ) ;
62- } else {
63- // FIXME: Support `instanceof Class`, falls back to Object for now. The fallback causes undefined behavior!
64- TransformerLogger ( ) . overloadNonLiteralParameterNotSupported ( signatureType . getText ( ) ) ;
65- return ts . createBinary ( identifier , ts . SyntaxKind . InstanceOfKeyword , ts . createIdentifier ( 'Object' ) ) ;
6696 }
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' ) ) ;
67106}
68107
69- function CreateUnionTypeOfEquality ( signatureType : ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration ) : ts . Expression {
70- 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 > = [ ] ;
71110
72111 if ( signatureType ) {
73- if ( ts . isUnionTypeNode ( signatureType ) ) {
74- typeNodes . push ( ...signatureType . types ) ;
112+ if ( ts . isTypeNode ( signatureType ) && ts . isUnionTypeNode ( signatureType ) ) {
113+ typeNodesAndVariableReferences . push ( ...signatureType . types ) ;
75114 } else {
76- typeNodes . push ( signatureType ) ;
115+ typeNodesAndVariableReferences . push ( signatureType ) ;
77116 }
78117 }
79118
80- const [ firstType , ...remainingTypes ] : ts . TypeNode [ ] = typeNodes ;
119+ const [ firstType , ...remainingTypes ] : Array < ts . TypeNode | ts . Identifier > = typeNodesAndVariableReferences ;
81120
82121 return remainingTypes . reduce (
83122 ( prevStatement : ts . Expression , typeNode : ts . TypeNode ) =>
@@ -89,22 +128,53 @@ function CreateUnionTypeOfEquality(signatureType: ts.TypeNode | undefined, prima
89128 ) ;
90129}
91130
92- 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 {
93138 const [ firstDeclaration , ...remainingDeclarations ] : Array < ts . ParameterDeclaration | undefined > = declarations ;
94139
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.
95161 const condition : ts . Expression = remainingDeclarations . reduce (
96162 ( prevStatement : ts . Expression , declaration : ts . ParameterDeclaration , index : number ) =>
97163 ts . createLogicalAnd (
98164 prevStatement ,
99- CreateUnionTypeOfEquality ( declaration . type , allDeclarations [ index + 1 ] ) ,
165+ CreateUnionTypeOfEquality ( variableReferenceOrType ( declaration ) , allDeclarations [ index + 1 ] ) ,
100166 ) ,
101- CreateUnionTypeOfEquality ( firstDeclaration ?. type , allDeclarations [ 0 ] ) ,
167+ CreateUnionTypeOfEquality ( variableReferenceOrType ( firstDeclaration ) , allDeclarations [ 0 ] ) ,
102168 ) ;
103169
104170 return ts . createIf ( condition , ts . createReturn ( returnValue ) , elseBranch ) ;
105171}
106172
107- 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 {
108178 const transformOverloadsOption : TsAutoMockOverloadOptions = GetTsAutoMockOverloadOptions ( ) ;
109179
110180 const [ signature , ...remainingSignatures ] : MethodSignature [ ] = signatures . filter ( ( _ : unknown , notFirst : number ) => transformOverloadsOption || ! notFirst ) ;
@@ -114,10 +184,10 @@ export function ResolveSignatureElseBranch(signatures: MethodSignature[], longes
114184 return ts . createReturn ( signature . returnValue ) ;
115185 }
116186
117- const elseBranch : ts . Statement = ResolveSignatureElseBranch ( remainingSignatures , longestParameterList ) ;
187+ const elseBranch : ts . Statement = ResolveSignatureElseBranch ( declarationVariableMap , remainingSignatures , longestParameterList ) ;
118188
119189 const currentParameters : ts . ParameterDeclaration [ ] = signature . parameters || [ ] ;
120- return ResolveParameterBranch ( currentParameters , longestParameterList , signature . returnValue , elseBranch ) ;
190+ return ResolveParameterBranch ( declarationVariableMap , currentParameters , longestParameterList , signature . returnValue , elseBranch ) ;
121191}
122192
123193function CreateProviderGetMethod ( ) : ts . PropertyAccessExpression {
0 commit comments