Skip to content

Commit

Permalink
make sure type query of module works for inferred variable type
Browse files Browse the repository at this point in the history
  • Loading branch information
Pmyl committed Jan 3, 2020
1 parent 1ed876b commit ebfcdf9
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 235 deletions.
3 changes: 2 additions & 1 deletion src/transformer/descriptor/module/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { GetDescriptor } from '../descriptor';
import { TypescriptHelper } from '../helper/helper';
import { GetMockPropertiesFromDeclarations } from '../mock/mockProperties';
import { PropertyLike } from '../mock/propertyLike';
import { GetTypeQueryDescriptorFromDeclaration } from '../typeQuery/typeQuery';

export function GetModuleDescriptor(node: ts.NamedDeclaration, scope: Scope): ts.Expression {
const typeChecker: ts.TypeChecker = TypeChecker();
Expand All @@ -30,5 +31,5 @@ export function GetModuleDescriptor(node: ts.NamedDeclaration, scope: Scope): ts
return GetMockPropertiesFromDeclarations(properties, [], scope);
}

return GetDescriptor(ts.createTypeQueryNode(externalModuleDeclaration.name as ts.Identifier), scope);
return GetTypeQueryDescriptorFromDeclaration(externalModuleDeclaration, scope);
}
130 changes: 77 additions & 53 deletions src/transformer/descriptor/typeQuery/typeQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,65 +9,89 @@ import { TypescriptHelper } from '../helper/helper';
import { GetMethodDeclarationDescriptor } from '../method/methodDeclaration';
import { GetModuleDescriptor } from '../module/module';
import { GetNullDescriptor } from '../null/null';
import { GetType } from '../type/type';
import { GetTypeReferenceDescriptor } from '../typeReference/typeReference';

export function GetTypeQueryDescriptor(node: ts.TypeQueryNode, scope: Scope): ts.Expression {
const typeChecker: ts.TypeChecker = TypeChecker();
const declaration: ts.Declaration = getTypeQueryDeclaration(node);
const declaration: ts.NamedDeclaration = getTypeQueryDeclaration(node);
return GetTypeQueryDescriptorFromDeclaration(declaration, scope);
}

export function GetTypeQueryDescriptorFromDeclaration(declaration: ts.NamedDeclaration, scope: Scope): ts.Expression {
const typeChecker: ts.TypeChecker = TypeChecker();

switch (declaration.kind) {
case ts.SyntaxKind.ClassDeclaration:
return TypescriptCreator.createFunctionExpressionReturn(
GetTypeReferenceDescriptor(
ts.createTypeReferenceNode(declaration.name as ts.Identifier, undefined),
scope,
),
);
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.InterfaceDeclaration:
return GetTypeReferenceDescriptor(
ts.createTypeReferenceNode(declaration.name as ts.Identifier, undefined),
scope,
);
case ts.SyntaxKind.NamespaceImport:
case ts.SyntaxKind.ImportEqualsDeclaration:
return GetModuleDescriptor(declaration, scope);
case ts.SyntaxKind.EnumDeclaration:
// TODO: Use following two lines when issue #17552 on typescript github is resolved (https://github.com/microsoft/TypeScript/issues/17552)
// TheNewEmitResolver.ensureEmitOf(GetImportDeclarationOf(node.eprName as ts.Identifier);
// return node.exprName as ts.Identifier;
return GetMockFactoryCallTypeofEnum(declaration as ts.EnumDeclaration);
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.MethodSignature:
return GetMethodDeclarationDescriptor(declaration as ts.FunctionDeclaration, scope);
case ts.SyntaxKind.VariableDeclaration:
const variable: ts.VariableDeclaration = declaration as ts.VariableDeclaration;

if (variable.type) {
return GetDescriptor(variable.type, scope);
}

const inferredType: ts.Node = GetType(variable.initializer, scope);
const symbol: ts.Symbol = typeChecker.getSymbolAtLocation(inferredType);

if (symbol) {
const inferredTypeDeclaration: ts.NamedDeclaration = getTypeQueryDeclarationFromSymbol(symbol);

return GetTypeQueryDescriptorFromDeclaration(inferredTypeDeclaration, scope);
} else {
return GetDescriptor(inferredType, scope);
}
default:
TransformerLogger().typeNotSupported(`TypeQuery of ${ts.SyntaxKind[declaration.kind]}`);
return GetNullDescriptor();
}
}

function getTypeQueryDeclaration(node: ts.TypeQueryNode): ts.NamedDeclaration {
const typeChecker: ts.TypeChecker = TypeChecker();
/*
TODO: Find different workaround without casting to any
Cast to any is been done because getSymbolAtLocation doesn't work when the node is an inferred identifier of a type query of a type query
Use case is:
```
const myVar = MyEnum;
createMock<typeof myVar>();
```
here `typeof myVar` is inferred `typeof MyEnum` and the `MyEnum` identifier doesn't play well with getSymbolAtLocation and it returns undefined.
*/
// tslint:disable-next-line no-any
const symbol: ts.Symbol = typeChecker.getSymbolAtLocation(node.exprName) || (node.exprName as any).symbol;

switch (declaration.kind) {
case ts.SyntaxKind.ClassDeclaration:
return TypescriptCreator.createFunctionExpressionReturn(
GetTypeReferenceDescriptor(
ts.createTypeReferenceNode(node.exprName as ts.Identifier, undefined),
scope,
),
);
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.InterfaceDeclaration:
return GetTypeReferenceDescriptor(
ts.createTypeReferenceNode(node.exprName as ts.Identifier, undefined),
scope,
);
case ts.SyntaxKind.NamespaceImport:
case ts.SyntaxKind.ImportEqualsDeclaration:
return GetModuleDescriptor(declaration, scope);
case ts.SyntaxKind.EnumDeclaration:
// TODO: Use following two lines when issue #17552 on typescript github is resolved (https://github.com/microsoft/TypeScript/issues/17552)
// TheNewEmitResolver.ensureEmitOf(GetImportDeclarationOf(node.eprName as ts.Identifier);
// return node.exprName as ts.Identifier;
return GetMockFactoryCallTypeofEnum(declaration as ts.EnumDeclaration);
case ts.SyntaxKind.FunctionDeclaration:
case ts.SyntaxKind.MethodSignature:
return GetMethodDeclarationDescriptor(declaration as ts.FunctionDeclaration, scope);
case ts.SyntaxKind.VariableDeclaration:
const typeNode: ts.TypeNode = (declaration as ts.VariableDeclaration).type || typeChecker.typeToTypeNode(typeChecker.getTypeFromTypeNode(node));
return GetDescriptor(typeNode, scope);
default:
TransformerLogger().typeNotSupported(`TypeQuery of ${ts.SyntaxKind[declaration.kind]}`);
return GetNullDescriptor();
}
return getTypeQueryDeclarationFromSymbol(symbol);
}

function getTypeQueryDeclaration(node: ts.TypeQueryNode): ts.Declaration {
const typeChecker: ts.TypeChecker = TypeChecker();
/*
TODO: Find different workaround without casting to any
Cast to any is been done because getSymbolAtLocation doesn't work when the node is an inferred identifier of a type query of a type query
Use case is:
```
const myVar = MyEnum;
createMock<typeof myVar>();
```
here `typeof myVar` is inferred `typeof MyEnum` and the `MyEnum` identifier doesn't play well with getSymbolAtLocation and it returns undefined.
*/
// tslint:disable-next-line no-any
const symbol: ts.Symbol = typeChecker.getSymbolAtLocation(node.exprName as ts.Identifier) || (node.exprName as any).symbol;
const declaration: ts.Declaration = symbol.declarations[0];
function getTypeQueryDeclarationFromSymbol(symbol: ts.Symbol): ts.NamedDeclaration {
const declaration: ts.Declaration = symbol.declarations[0];

if (ts.isImportEqualsDeclaration(declaration)) {
return declaration;
}
if (ts.isImportEqualsDeclaration(declaration)) {
return declaration;
}

return TypescriptHelper.GetDeclarationFromSymbol(symbol);
return TypescriptHelper.GetDeclarationFromSymbol(symbol);
}
Loading

0 comments on commit ebfcdf9

Please sign in to comment.