Skip to content

Commit

Permalink
feat: moar types support
Browse files Browse the repository at this point in the history
  • Loading branch information
bradzacher committed May 5, 2020
1 parent c327102 commit 80487d5
Show file tree
Hide file tree
Showing 127 changed files with 2,783 additions and 966 deletions.
20 changes: 20 additions & 0 deletions packages/scope-manager/src/ScopeManager.ts
Expand Up @@ -4,10 +4,13 @@ import {
BlockScope,
CatchScope,
ClassScope,
ConditionalTypeScope,
ForScope,
FunctionExpressionNameScope,
FunctionScope,
FunctionTypeScope,
GlobalScope,
MappedTypeScope,
ModuleScope,
Scope,
SwitchScope,
Expand Down Expand Up @@ -170,6 +173,13 @@ class ScopeManager {
return this.nestScope(new ClassScope(this, this.currentScope, node));
}

public nestConditionalTypeScope(node: ConditionalTypeScope['block']): Scope {
assert(this.currentScope);
return this.nestScope(
new ConditionalTypeScope(this, this.currentScope, node),
);
}

public nestForScope(node: ForScope['block']): Scope {
assert(this.currentScope);
return this.nestScope(new ForScope(this, this.currentScope, node));
Expand All @@ -194,10 +204,20 @@ class ScopeManager {
);
}

public nestFunctionTypeScope(node: FunctionTypeScope['block']): Scope {
assert(this.currentScope);
return this.nestScope(new FunctionTypeScope(this, this.currentScope, node));
}

public nestGlobalScope(node: GlobalScope['block']): Scope {
return this.nestScope(new GlobalScope(this, node));
}

public nestMappedTypeScope(node: MappedTypeScope['block']): Scope {
assert(this.currentScope);
return this.nestScope(new MappedTypeScope(this, this.currentScope, node));
}

public nestModuleScope(node: ModuleScope['block']): Scope {
assert(this.currentScope);
return this.nestScope(new ModuleScope(this, this.currentScope, node));
Expand Down
2 changes: 1 addition & 1 deletion packages/scope-manager/src/referencer/Reference.ts
Expand Up @@ -55,7 +55,7 @@ class Reference {
*/
public readonly writeExpr?: TSESTree.Node | null;

public maybeImplicitGlobal?: ReferenceImplicitGlobal | null;
public readonly maybeImplicitGlobal?: ReferenceImplicitGlobal | null;

/**
* True if this reference was created from a type context, false otherwise
Expand Down
159 changes: 134 additions & 25 deletions packages/scope-manager/src/referencer/TypeVisitor.ts
Expand Up @@ -19,44 +19,158 @@ class TypeVisitor extends Visitor {
typeReferencer.visit(node);
}

protected visitTypeDeclaration(
name: TSESTree.Identifier,
node: TSESTree.TSInterfaceDeclaration | TSESTree.TSTypeAliasDeclaration,
): void {
this.referencer
.currentScope()
.defineIdentifier(name, new TypeDefinition(name, node));
///////////////////
// Visit helpers //
///////////////////

protected visitFunctionType(
node:
| TSESTree.TSCallSignatureDeclaration
| TSESTree.TSConstructorType
| TSESTree.TSConstructSignatureDeclaration
| TSESTree.TSFunctionType
| TSESTree.TSMethodSignature,
): void {
// type parameters cannot be referenced from outside their current scope
this.referencer.scopeManager.nestTypeScope(node);
if (node.typeParameters) {
this.referencer.scopeManager.nestFunctionTypeScope(node);
this.visit(node.typeParameters);
}

this.visit(node.typeParameters);
for (const param of node.params) {
if (param.type == AST_NODE_TYPES.Identifier) {
this.visit(param.typeAnnotation);
}
}
this.visit(node.returnType);

if (node.typeParameters) {
this.referencer.close(node);
}
}

protected TSTypeParameter(node: TSESTree.TSTypeParameter): void {
this.referencer
.currentScope()
.defineIdentifier(node.name, new TypeDefinition(node.name, node));
protected visitPropertyKey(
node: TSESTree.TSMethodSignature | TSESTree.TSPropertySignature,
): void {
if (node.computed && node.key.type === AST_NODE_TYPES.Identifier) {
this.referencer.currentScope().referenceValue(node.key);
}
}
protected TSTypeAliasDeclaration(
node: TSESTree.TSTypeAliasDeclaration,

/////////////////////
// Visit selectors //
/////////////////////

protected Identifier(node: TSESTree.Identifier): void {
this.referencer.currentScope().referenceType(node);
}

protected TSCallSignatureDeclaration(
node: TSESTree.TSCallSignatureDeclaration,
): void {
this.visitTypeDeclaration(node.id, node);
this.visit(node.typeAnnotation);
this.visitFunctionType(node);
}

protected TSConditionalType(node: TSESTree.TSConditionalType): void {
// conditional types can define inferred type parameters
// which are only accessible from inside the conditional parameter
this.referencer.scopeManager.nestConditionalTypeScope(node);

this.visitChildren(node);

this.referencer.close(node);
}

protected TSConstructorType(node: TSESTree.TSConstructorType): void {
this.visitFunctionType(node);
}

protected TSConstructSignatureDeclaration(
node: TSESTree.TSConstructSignatureDeclaration,
): void {
this.visitFunctionType(node);
}

protected TSFunctionType(node: TSESTree.TSFunctionType): void {
this.visitFunctionType(node);
}

protected TSIndexSignature(node: TSESTree.TSIndexSignature): void {
for (const param of node.parameters) {
if (param.type === AST_NODE_TYPES.Identifier) {
this.visit(param.typeAnnotation);
}
}
this.visit(node.typeAnnotation);
}

protected TSInterfaceDeclaration(
node: TSESTree.TSInterfaceDeclaration,
): void {
this.visitTypeDeclaration(node.id, node);
this.referencer
.currentScope()
.defineIdentifier(node.id, new TypeDefinition(node.id, node));

// type parameters cannot be referenced from outside their current scope
if (node.typeParameters) {
this.referencer.scopeManager.nestTypeScope(node);
this.visit(node.typeParameters);
}

node.extends?.forEach(this.visit, this);
node.implements?.forEach(this.visit, this);
this.visit(node.body);

if (node.typeParameters) {
this.referencer.close(node);
}
}

protected TSMappedType(node: TSESTree.TSMappedType): void {
this.referencer.scopeManager.nestMappedTypeScope(node);
this.visitChildren(node);
this.referencer.close(node);
}

protected Identifier(node: TSESTree.Identifier): void {
this.referencer.currentScope().referenceType(node);
protected TSMethodSignature(node: TSESTree.TSMethodSignature): void {
this.visitPropertyKey(node);
this.visitFunctionType(node);
}

protected TSPropertySignature(node: TSESTree.TSPropertySignature): void {
this.visitPropertyKey(node);
this.visit(node.typeAnnotation);
}

protected TSQualifiedName(node: TSESTree.TSQualifiedName): void {
this.visit(node.left);
// we don't visit the right as it a name on the thing, not a name to reference
}

protected TSTypeAliasDeclaration(
node: TSESTree.TSTypeAliasDeclaration,
): void {
this.referencer
.currentScope()
.defineIdentifier(node.id, new TypeDefinition(node.id, node));

// type parameters cannot be referenced from outside their current scope
if (node.typeParameters) {
this.referencer.scopeManager.nestTypeScope(node);
this.visit(node.typeParameters);
}

this.visit(node.typeAnnotation);

if (node.typeParameters) {
this.referencer.close(node);
}
}

protected TSTypeParameter(node: TSESTree.TSTypeParameter): void {
this.referencer
.currentScope()
.defineIdentifier(node.name, new TypeDefinition(node.name, node));
}

// a type query `typeof foo` is a special case that references a _non-type_ variable,
Expand All @@ -71,11 +185,6 @@ class TypeVisitor extends Visitor {
this.referencer.currentScope().referenceValue(expr);
}
}

protected TSQualifiedName(node: TSESTree.TSQualifiedName): void {
this.visit(node.left);
// we don't visit the right as it a name
}
}

export { TypeVisitor };
21 changes: 21 additions & 0 deletions packages/scope-manager/src/scope/ConditionalTypeScope.ts
@@ -0,0 +1,21 @@
import { TSESTree } from '@typescript-eslint/experimental-utils';
import { Scope } from './Scope';
import { ScopeBase } from './ScopeBase';
import { ScopeType } from './ScopeType';
import { ScopeManager } from '../ScopeManager';

class ConditionalTypeScope extends ScopeBase<
ScopeType.conditionalType,
TSESTree.TSConditionalType,
Scope
> {
constructor(
scopeManager: ScopeManager,
upperScope: ConditionalTypeScope['upper'],
block: ConditionalTypeScope['block'],
) {
super(scopeManager, ScopeType.conditionalType, upperScope, block, false);
}
}

export { ConditionalTypeScope };
25 changes: 25 additions & 0 deletions packages/scope-manager/src/scope/FunctionTypeScope.ts
@@ -0,0 +1,25 @@
import { TSESTree } from '@typescript-eslint/experimental-utils';
import { Scope } from './Scope';
import { ScopeBase } from './ScopeBase';
import { ScopeType } from './ScopeType';
import { ScopeManager } from '../ScopeManager';

class FunctionTypeScope extends ScopeBase<
ScopeType.functionType,
| TSESTree.TSCallSignatureDeclaration
| TSESTree.TSConstructorType
| TSESTree.TSConstructSignatureDeclaration
| TSESTree.TSFunctionType
| TSESTree.TSMethodSignature,
Scope
> {
constructor(
scopeManager: ScopeManager,
upperScope: FunctionTypeScope['upper'],
block: FunctionTypeScope['block'],
) {
super(scopeManager, ScopeType.functionType, upperScope, block, false);
}
}

export { FunctionTypeScope };
21 changes: 21 additions & 0 deletions packages/scope-manager/src/scope/MappedTypeScope.ts
@@ -0,0 +1,21 @@
import { TSESTree } from '@typescript-eslint/experimental-utils';
import { Scope } from './Scope';
import { ScopeBase } from './ScopeBase';
import { ScopeType } from './ScopeType';
import { ScopeManager } from '../ScopeManager';

class MappedTypeScope extends ScopeBase<
ScopeType.mappedType,
TSESTree.TSMappedType,
Scope
> {
constructor(
scopeManager: ScopeManager,
upperScope: MappedTypeScope['upper'],
block: MappedTypeScope['block'],
) {
super(scopeManager, ScopeType.mappedType, upperScope, block, false);
}
}

export { MappedTypeScope };
10 changes: 7 additions & 3 deletions packages/scope-manager/src/scope/Scope.ts
@@ -1,10 +1,13 @@
import { BlockScope } from './BlockScope';
import { CatchScope } from './CatchScope';
import { ClassScope } from './ClassScope';
import { ConditionalTypeScope } from './ConditionalTypeScope';
import { ForScope } from './ForScope';
import { FunctionExpressionNameScope } from './FunctionExpressionNameScope';
import { FunctionScope } from './FunctionScope';
import { FunctionTypeScope } from './FunctionTypeScope';
import { GlobalScope } from './GlobalScope';
import { MappedTypeScope } from './MappedTypeScope';
import { ModuleScope } from './ModuleScope';
import { SwitchScope } from './SwitchScope';
import { TypeScope } from './TypeScope';
Expand All @@ -14,15 +17,16 @@ type Scope =
| BlockScope
| CatchScope
| ClassScope
| ConditionalTypeScope
| ForScope
| FunctionExpressionNameScope
| FunctionScope
| FunctionTypeScope
| GlobalScope
| MappedTypeScope
| ModuleScope
| SwitchScope
| TypeScope
| WithScope;

type BlockNode = Scope['block'];

export { BlockNode, Scope };
export { Scope };
16 changes: 11 additions & 5 deletions packages/scope-manager/src/scope/ScopeBase.ts
Expand Up @@ -6,7 +6,7 @@ import { FunctionScope } from './FunctionScope';
import { GlobalScope } from './GlobalScope';
import { ScopeType } from './ScopeType';
import { ScopeManager } from '../ScopeManager';
import { BlockNode, Scope } from './Scope';
import { Scope } from './Scope';
import { ModuleScope } from './ModuleScope';
import { assert } from '../assert';
import { Definition, DefinitionType } from '../definition';
Expand All @@ -23,7 +23,7 @@ import { Variable } from '../Variable';
*/
function isStrictScope(
scope: Scope,
block: BlockNode,
block: TSESTree.Node,
isMethodDefinition: boolean,
): boolean {
let body: TSESTree.BlockStatement | TSESTree.Program | null | undefined;
Expand All @@ -37,7 +37,13 @@ function isStrictScope(
return true;
}

if (scope.type === ScopeType.class || scope.type === ScopeType.module) {
if (
scope.type === ScopeType.class ||
scope.type === ScopeType.module ||
scope.type === ScopeType.type ||
scope.type === ScopeType.conditionalType ||
scope.type === ScopeType.functionType
) {
return true;
}

Expand Down Expand Up @@ -130,10 +136,10 @@ function shouldBeStaticallyClosed(def: Definition): boolean {

const generator = createIdGenerator();

type AnyScope = ScopeBase<ScopeType, BlockNode, Scope | null>;
type AnyScope = ScopeBase<ScopeType, TSESTree.Node, Scope | null>;
abstract class ScopeBase<
TType extends ScopeType,
TBlock extends BlockNode,
TBlock extends TSESTree.Node,
TUpper extends Scope | null
> {
/**
Expand Down

0 comments on commit 80487d5

Please sign in to comment.