1
1
import ts from 'typescript' ;
2
- import { GetTsAutoMockOverloadOptions , TsAutoMockOverloadOptions } from '../../../options/overload' ;
3
- import { TypescriptCreator } from '../../helper/creator' ;
4
- import { MockDefiner } from '../../mockDefiner/mockDefiner' ;
5
- import { ModuleName } from '../../mockDefiner/modules/moduleName' ;
6
- import { MockIdentifierGenericParameterValue } from '../../mockIdentifier/mockIdentifier' ;
2
+ import { MethodSignature , TypescriptCreator } from '../../helper/creator' ;
7
3
import { Scope } from '../../scope/scope' ;
4
+ import { ResolveSignatureElseBranch } from '../helper/branching' ;
8
5
import { TypescriptHelper } from '../helper/helper' ;
9
6
import { GetNullDescriptor } from '../null/null' ;
10
- import { GetDescriptor } from '../descriptor' ;
11
7
import { GetTypeParameterDescriptor } from '../typeParameter/typeParameter' ;
12
8
13
- export interface MethodSignature {
14
- parameters ?: ts . TypeNode [ ] ;
15
- returnValue : ts . Expression ;
16
- }
17
-
18
9
function isDeclarationWithTypeParameterChildren ( node : ts . Node ) : node is ts . DeclarationWithTypeParameterChildren {
19
10
return ts . isFunctionLike ( node ) ||
20
11
ts . isClassLike ( node ) ||
@@ -48,12 +39,16 @@ export function GetConditionalTypeDescriptor(node: ts.ConditionalTypeNode, scope
48
39
return GetNullDescriptor ( ) ;
49
40
}
50
41
42
+ const statements : ts . Statement [ ] = [ ] ;
43
+
51
44
const genericValue : ts . CallExpression = GetTypeParameterDescriptor ( declaration , scope ) ;
52
45
53
- const statements : ts . Statement [ ] = [ ] ;
46
+ const signatures : MethodSignature [ ] = ConstructSignatures ( node ) ;
47
+ const [ signature ] : MethodSignature [ ] = signatures ;
54
48
49
+ const parameterIdentifier : ts . Identifier = TypescriptHelper . ExtractFirstIdentifier ( signature . parameters [ 0 ] . name ) ;
55
50
const valueDeclaration : ts . VariableDeclaration = TypescriptCreator . createVariableDeclaration (
56
- MockIdentifierGenericParameterValue ,
51
+ parameterIdentifier ,
57
52
genericValue ,
58
53
) ;
59
54
@@ -63,157 +58,43 @@ export function GetConditionalTypeDescriptor(node: ts.ConditionalTypeNode, scope
63
58
] ) ,
64
59
) ;
65
60
66
- statements . push ( ResolveSignatureElseBranch ( new Map ( ) , ConstructSignatures ( node , scope ) , [ valueDeclaration ] ) ) ;
61
+ const typeVariableMap : Map < ts . TypeNode , ts . StringLiteral | ts . Identifier > = new Map (
62
+ signatures . reduce ( ( typeHashTuples : [ ts . TypeNode , ts . StringLiteral ] [ ] , s : MethodSignature ) => {
63
+ const [ parameter ] : typeof s . parameters | [ undefined ] = s . parameters ;
64
+ if ( ! parameter ) {
65
+ return typeHashTuples ;
66
+ }
67
+
68
+ if ( ts . isFunctionLike ( parameter . type ) ) {
69
+ typeHashTuples . push ( [
70
+ parameter . type ,
71
+ ts . createStringLiteral (
72
+ TypescriptCreator . createSignatureHash ( parameter . type ) ,
73
+ ) ,
74
+ ] ) ;
75
+ }
76
+
77
+ return typeHashTuples ;
78
+ } , [ ] as [ ts . TypeNode , ts . StringLiteral ] [ ] ) ,
79
+ ) ;
80
+
81
+ statements . push ( ResolveSignatureElseBranch ( typeVariableMap , signatures , signature , scope ) ) ;
67
82
68
83
return TypescriptCreator . createIIFE ( ts . createBlock ( statements , true ) ) ;
69
84
}
70
85
71
- function ConstructSignatures ( node : ts . ConditionalTypeNode , scope : Scope , signatures : MethodSignature [ ] = [ ] ) : MethodSignature [ ] {
72
- const parameters : ts . TypeNode [ ] = [ node . extendsType ] ;
73
-
86
+ function ConstructSignatures ( node : ts . ConditionalTypeNode , signatures : MethodSignature [ ] = [ ] ) : MethodSignature [ ] {
74
87
if ( ts . isConditionalTypeNode ( node . trueType ) ) {
75
- return ConstructSignatures ( node . trueType , scope , signatures ) ;
88
+ return ConstructSignatures ( node . trueType , signatures ) ;
76
89
}
77
90
78
- signatures . push ( {
79
- parameters,
80
- returnValue : GetDescriptor ( node . trueType , scope ) ,
81
- } ) ;
91
+ signatures . push ( TypescriptCreator . createMethodSignature ( [ node . extendsType ] , node . trueType ) ) ;
82
92
83
93
if ( ts . isConditionalTypeNode ( node . falseType ) ) {
84
- return ConstructSignatures ( node . falseType , scope , signatures ) ;
94
+ return ConstructSignatures ( node . falseType , signatures ) ;
85
95
}
86
96
87
- signatures . push ( {
88
- parameters,
89
- returnValue : GetDescriptor ( node . falseType , scope ) ,
90
- } ) ;
97
+ signatures . push ( TypescriptCreator . createMethodSignature ( undefined , node . falseType ) ) ;
91
98
92
99
return signatures ;
93
100
}
94
-
95
- function CreateTypeEquality ( signatureType : ts . Identifier | ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration | ts . VariableDeclaration ) : ts . Expression {
96
- // TODO: Factor this into a helper - guess it can be helpful in other places.
97
- let declarationName : ts . BindingName = primaryDeclaration . name ;
98
-
99
- while ( ! ts . isIdentifier ( declarationName ) ) {
100
- const [ bindingElement ] : Array < ts . BindingElement | undefined > = ( declarationName . elements as ts . NodeArray < ts . ArrayBindingElement > ) . filter ( ts . isBindingElement ) ;
101
- if ( ! bindingElement ) {
102
- throw new Error ( 'Failed to find an identifier for the primary declaration!' ) ;
103
- }
104
-
105
- declarationName = bindingElement . name ;
106
- }
107
-
108
- if ( ! signatureType ) {
109
- return ts . createPrefix (
110
- ts . SyntaxKind . ExclamationToken ,
111
- ts . createPrefix (
112
- ts . SyntaxKind . ExclamationToken ,
113
- declarationName ,
114
- ) ,
115
- ) ;
116
- }
117
-
118
- if ( TypescriptHelper . IsLiteralOrPrimitive ( signatureType ) ) {
119
- return ts . createStrictEquality (
120
- ts . createTypeOf ( declarationName ) ,
121
- signatureType ? ts . createStringLiteral ( signatureType . getText ( ) ) : ts . createVoidZero ( ) ,
122
- ) ;
123
- }
124
-
125
- if ( ts . isIdentifier ( signatureType ) ) {
126
- return ts . createStrictEquality (
127
- ts . createPropertyAccess ( declarationName , '__factory' ) ,
128
- signatureType ,
129
- ) ;
130
- }
131
-
132
- return ts . createBinary ( declarationName , ts . SyntaxKind . InstanceOfKeyword , ts . createIdentifier ( 'Object' ) ) ;
133
- }
134
-
135
- function CreateUnionTypeOfEquality ( signatureType : ts . Identifier | ts . TypeNode | undefined , primaryDeclaration : ts . ParameterDeclaration | ts . VariableDeclaration ) : ts . Expression {
136
- const typeNodesAndVariableReferences : Array < ts . TypeNode | ts . Identifier > = [ ] ;
137
-
138
- if ( signatureType ) {
139
- if ( ts . isTypeNode ( signatureType ) && ts . isUnionTypeNode ( signatureType ) ) {
140
- typeNodesAndVariableReferences . push ( ...signatureType . types ) ;
141
- } else {
142
- typeNodesAndVariableReferences . push ( signatureType ) ;
143
- }
144
- }
145
-
146
- const [ firstType , ...remainingTypes ] : Array < ts . TypeNode | ts . Identifier > = typeNodesAndVariableReferences ;
147
-
148
- return remainingTypes . reduce (
149
- ( prevStatement : ts . Expression , typeNode : ts . TypeNode ) =>
150
- ts . createLogicalOr (
151
- prevStatement ,
152
- CreateTypeEquality ( typeNode , primaryDeclaration ) ,
153
- ) ,
154
- CreateTypeEquality ( firstType , primaryDeclaration ) ,
155
- ) ;
156
- }
157
-
158
- function ResolveParameterBranch (
159
- declarationVariableMap : Map < ts . Declaration , ts . Identifier > ,
160
- declarations : ts . TypeNode [ ] ,
161
- allDeclarations : Array < ts . ParameterDeclaration | ts . VariableDeclaration > ,
162
- returnValue : ts . Expression ,
163
- elseBranch : ts . Statement ,
164
- ) : ts . Statement {
165
- const [ firstDeclaration , ...remainingDeclarations ] : Array < ts . TypeNode | undefined > = declarations ;
166
-
167
- const variableReferenceOrType : ( t : ts . TypeNode | undefined ) => ts . Identifier | ts . TypeNode | undefined = ( t : ts . TypeNode | undefined ) => t ;
168
- // const variableReferenceOrType: (declaration: ts.ParameterDeclaration) => ts.Identifier | ts.TypeNode | undefined =
169
- // (declaration: ts.ParameterDeclaration) => {
170
- // if (declarationVariableMap.has(declaration)) {
171
- // return declarationVariableMap.get(declaration);
172
- // } else {
173
- // return declaration.type;
174
- // }
175
- // };
176
-
177
- // TODO: These conditions quickly grow in size, but it should be possible to
178
- // squeeze things together and optimize it with something like:
179
- //
180
- // const typeOf = function (left, right) { return typeof left === right; }
181
- // const evaluate = (function(left, right) { return this._ = this._ || typeOf(left, right); }).bind({})
182
- //
183
- // if (evaluate(firstArg, 'boolean') && evaluate(secondArg, 'number') && ...) {
184
- // ...
185
- // }
186
- //
187
- // `this._' acts as a cache, since the control flow may evaluate the same
188
- // conditions multiple times.
189
- const condition : ts . Expression = remainingDeclarations . reduce (
190
- ( prevStatement : ts . Expression , node : ts . TypeNode | undefined , index : number ) =>
191
- ts . createLogicalAnd (
192
- prevStatement ,
193
- CreateUnionTypeOfEquality ( variableReferenceOrType ( node ) , allDeclarations [ index + 1 ] ) ,
194
- ) ,
195
- CreateUnionTypeOfEquality ( variableReferenceOrType ( firstDeclaration ) , allDeclarations [ 0 ] ) ,
196
- ) ;
197
-
198
- return ts . createIf ( condition , ts . createReturn ( returnValue ) , elseBranch ) ;
199
- }
200
-
201
- export function ResolveSignatureElseBranch (
202
- declarationVariableMap : Map < ts . ParameterDeclaration , ts . Identifier > ,
203
- signatures : MethodSignature [ ] ,
204
- longestParameterList : Array < ts . ParameterDeclaration | ts . VariableDeclaration > ,
205
- ) : ts . Statement {
206
- const transformOverloadsOption : TsAutoMockOverloadOptions = GetTsAutoMockOverloadOptions ( ) ;
207
-
208
- const [ signature , ...remainingSignatures ] : MethodSignature [ ] = signatures . filter ( ( _ : unknown , notFirst : number ) => transformOverloadsOption || ! notFirst ) ;
209
-
210
- const indistinctSignatures : boolean = signatures . every ( ( sig : MethodSignature ) => ! sig . parameters ?. length ) ;
211
- if ( ! remainingSignatures . length || indistinctSignatures ) {
212
- return ts . createReturn ( signature . returnValue ) ;
213
- }
214
-
215
- const elseBranch : ts . Statement = ResolveSignatureElseBranch ( declarationVariableMap , remainingSignatures , longestParameterList ) ;
216
-
217
- const currentParameters : ts . TypeNode [ ] = signature . parameters || [ ] ;
218
- return ResolveParameterBranch ( declarationVariableMap , currentParameters , longestParameterList , signature . returnValue , elseBranch ) ;
219
- }
0 commit comments