11import 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' ;
73import { Scope } from '../../scope/scope' ;
4+ import { ResolveSignatureElseBranch } from '../helper/branching' ;
85import { TypescriptHelper } from '../helper/helper' ;
96import { GetNullDescriptor } from '../null/null' ;
10- import { GetDescriptor } from '../descriptor' ;
117import { GetTypeParameterDescriptor } from '../typeParameter/typeParameter' ;
128
13- export interface MethodSignature {
14- parameters ?: ts . TypeNode [ ] ;
15- returnValue : ts . Expression ;
16- }
17-
189function isDeclarationWithTypeParameterChildren ( node : ts . Node ) : node is ts . DeclarationWithTypeParameterChildren {
1910 return ts . isFunctionLike ( node ) ||
2011 ts . isClassLike ( node ) ||
@@ -48,12 +39,16 @@ export function GetConditionalTypeDescriptor(node: ts.ConditionalTypeNode, scope
4839 return GetNullDescriptor ( ) ;
4940 }
5041
42+ const statements : ts . Statement [ ] = [ ] ;
43+
5144 const genericValue : ts . CallExpression = GetTypeParameterDescriptor ( declaration , scope ) ;
5245
53- const statements : ts . Statement [ ] = [ ] ;
46+ const signatures : MethodSignature [ ] = ConstructSignatures ( node ) ;
47+ const [ signature ] : MethodSignature [ ] = signatures ;
5448
49+ const parameterIdentifier : ts . Identifier = TypescriptHelper . ExtractFirstIdentifier ( signature . parameters [ 0 ] . name ) ;
5550 const valueDeclaration : ts . VariableDeclaration = TypescriptCreator . createVariableDeclaration (
56- MockIdentifierGenericParameterValue ,
51+ parameterIdentifier ,
5752 genericValue ,
5853 ) ;
5954
@@ -63,157 +58,43 @@ export function GetConditionalTypeDescriptor(node: ts.ConditionalTypeNode, scope
6358 ] ) ,
6459 ) ;
6560
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 ) ) ;
6782
6883 return TypescriptCreator . createIIFE ( ts . createBlock ( statements , true ) ) ;
6984}
7085
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 [ ] {
7487 if ( ts . isConditionalTypeNode ( node . trueType ) ) {
75- return ConstructSignatures ( node . trueType , scope , signatures ) ;
88+ return ConstructSignatures ( node . trueType , signatures ) ;
7689 }
7790
78- signatures . push ( {
79- parameters,
80- returnValue : GetDescriptor ( node . trueType , scope ) ,
81- } ) ;
91+ signatures . push ( TypescriptCreator . createMethodSignature ( [ node . extendsType ] , node . trueType ) ) ;
8292
8393 if ( ts . isConditionalTypeNode ( node . falseType ) ) {
84- return ConstructSignatures ( node . falseType , scope , signatures ) ;
94+ return ConstructSignatures ( node . falseType , signatures ) ;
8595 }
8696
87- signatures . push ( {
88- parameters,
89- returnValue : GetDescriptor ( node . falseType , scope ) ,
90- } ) ;
97+ signatures . push ( TypescriptCreator . createMethodSignature ( undefined , node . falseType ) ) ;
9198
9299 return signatures ;
93100}
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