fix(language-service): Update types for TypeScript nullability support

This commit is contained in:
Miško Hevery 2017-03-24 09:57:32 -07:00 committed by Hans
parent 6f5fccfeb7
commit 540581da3e
28 changed files with 234 additions and 225 deletions

View File

@ -111,7 +111,7 @@ class _Visitor implements html.Visitor {
this._translations = translations;
// Construct a single fake root element
const wrapper = new html.Element('wrapper', [], nodes, undefined, undefined, undefined);
const wrapper = new html.Element('wrapper', [], nodes, undefined !, undefined, undefined);
const translatedNode = wrapper.visit(this, null);

View File

@ -9,12 +9,12 @@
import {ParseSourceSpan} from '../parse_util';
export interface Node {
sourceSpan: ParseSourceSpan|null;
sourceSpan: ParseSourceSpan;
visit(visitor: Visitor, context: any): any;
}
export class Text implements Node {
constructor(public value: string, public sourceSpan: ParseSourceSpan|null) {}
constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
visit(visitor: Visitor, context: any): any { return visitor.visitText(this, context); }
}
@ -43,8 +43,7 @@ export class Attribute implements Node {
export class Element implements Node {
constructor(
public name: string, public attrs: Attribute[], public children: Node[],
public sourceSpan: ParseSourceSpan|null = null,
public startSourceSpan: ParseSourceSpan|null = null,
public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null = null,
public endSourceSpan: ParseSourceSpan|null = null) {}
visit(visitor: Visitor, context: any): any { return visitor.visitElement(this, context); }
}

View File

@ -19,7 +19,7 @@ export interface TemplateAst {
/**
* The source span from which this node was parsed.
*/
sourceSpan: ParseSourceSpan|null;
sourceSpan: ParseSourceSpan;
/**
* Visit this node and possibly transform it.
@ -127,7 +127,7 @@ export class ElementAst implements TemplateAst {
public directives: DirectiveAst[], public providers: ProviderAst[],
public hasViewContainer: boolean, public queryMatches: QueryMatch[],
public children: TemplateAst[], public ngContentIndex: number|null,
public sourceSpan: ParseSourceSpan|null, public endSourceSpan: ParseSourceSpan|null) {}
public sourceSpan: ParseSourceSpan, public endSourceSpan: ParseSourceSpan|null) {}
visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitElement(this, context);

View File

@ -391,7 +391,7 @@ class TemplateParseVisitor implements html.Visitor {
nodeName, attrs, elementProps, events, references,
providerContext.transformedDirectiveAsts, providerContext.transformProviders,
providerContext.transformedHasViewContainer, providerContext.queryMatches, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan || null,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan,
element.endSourceSpan || null);
}

View File

@ -25,5 +25,5 @@ export class AstPath<T> {
push(node: T) { this.path.push(node); }
pop(): T { return this.path.pop(); }
pop(): T { return this.path.pop() !; }
}

View File

@ -29,27 +29,27 @@ const hiddenHtmlElements = {
link: true,
};
export function getTemplateCompletions(templateInfo: TemplateInfo): Completions {
let result: Completions = undefined;
export function getTemplateCompletions(templateInfo: TemplateInfo): Completions|undefined {
let result: Completions|undefined = undefined;
let {htmlAst, templateAst, template} = templateInfo;
// The templateNode starts at the delimiter character so we add 1 to skip it.
let templatePosition = templateInfo.position - template.span.start;
let templatePosition = templateInfo.position ! - template.span.start;
let path = new HtmlAstPath(htmlAst, templatePosition);
let mostSpecific = path.tail;
if (path.empty) {
result = elementCompletions(templateInfo, path);
} else {
let astPosition = templatePosition - mostSpecific.sourceSpan.start.offset;
mostSpecific.visit(
let astPosition = templatePosition - mostSpecific !.sourceSpan !.start.offset;
mostSpecific !.visit(
{
visitElement(ast) {
let startTagSpan = spanOf(ast.sourceSpan);
let tagLen = ast.name.length;
if (templatePosition <=
startTagSpan.start + tagLen + 1 /* 1 for the opening angle bracked */) {
startTagSpan !.start + tagLen + 1 /* 1 for the opening angle bracked */) {
// If we are in the tag then return the element completions.
result = elementCompletions(templateInfo, path);
} else if (templatePosition < startTagSpan.end) {
} else if (templatePosition < startTagSpan !.end) {
// We are in the attribute section of the element (but not in an attribute).
// Return the attribute completions.
result = attributeCompletions(templateInfo, path);
@ -65,7 +65,7 @@ export function getTemplateCompletions(templateInfo: TemplateInfo): Completions
},
visitText(ast) {
// Check if we are in a entity.
result = entityCompletions(getSourceText(template, spanOf(ast)), astPosition);
result = entityCompletions(getSourceText(template, spanOf(ast) !), astPosition);
if (result) return result;
result = interpolationCompletions(templateInfo, templatePosition);
if (result) return result;
@ -96,8 +96,8 @@ export function getTemplateCompletions(templateInfo: TemplateInfo): Completions
return result;
}
function attributeCompletions(info: TemplateInfo, path: HtmlAstPath): Completions {
let item = path.tail instanceof Element ? path.tail : path.parentOf(path.tail);
function attributeCompletions(info: TemplateInfo, path: HtmlAstPath): Completions|undefined {
let item = path.tail instanceof Element ? path.tail : path.parentOf(path.tail !);
if (item instanceof Element) {
return attributeCompletionsForElement(info, item.name, item);
}
@ -146,7 +146,7 @@ function getAttributeInfosForElement(
const selectorAndAttributeNames =
applicableSelectors.map(selector => ({selector, attrs: selector.attrs.filter(a => !!a)}));
let attrs = flatten(selectorAndAttributeNames.map<AttrInfo[]>(selectorAndAttr => {
const directive = selectorMap.get(selectorAndAttr.selector);
const directive = selectorMap.get(selectorAndAttr.selector) !;
const result = selectorAndAttr.attrs.map<AttrInfo>(
name => ({name, input: name in directive.inputs, output: name in directive.outputs}));
return result;
@ -165,7 +165,7 @@ function getAttributeInfosForElement(
// All input and output properties of the matching directives should be added.
let elementSelector = element ?
createElementCssSelector(element) :
createElementCssSelector(new Element(elementName, [], [], undefined, undefined, undefined));
createElementCssSelector(new Element(elementName, [], [], null !, null, null));
let matcher = new SelectorMatcher();
matcher.addSelectables(selectors);
@ -188,7 +188,7 @@ function getAttributeInfosForElement(
}
function attributeValueCompletions(
info: TemplateInfo, position: number, attr: Attribute): Completions {
info: TemplateInfo, position: number, attr: Attribute): Completions|undefined {
const path = new TemplateAstPath(info.templateAst, position);
const mostSpecific = path.tail;
if (mostSpecific) {
@ -209,7 +209,7 @@ function attributeValueCompletions(
}
}
function elementCompletions(info: TemplateInfo, path: HtmlAstPath): Completions {
function elementCompletions(info: TemplateInfo, path: HtmlAstPath): Completions|undefined {
let htmlNames = elementNames().filter(name => !(name in hiddenHtmlElements));
// Collect the elements referenced by the selectors
@ -217,18 +217,18 @@ function elementCompletions(info: TemplateInfo, path: HtmlAstPath): Completions
getSelectors(info).selectors.map(selector => selector.element).filter(name => !!name);
let components =
directiveElements.map<Completion>(name => ({kind: 'component', name: name, sort: name}));
directiveElements.map<Completion>(name => ({kind: 'component', name: name !, sort: name !}));
let htmlElements = htmlNames.map<Completion>(name => ({kind: 'element', name: name, sort: name}));
// Return components and html elements
return uniqueByName(htmlElements.concat(components));
}
function entityCompletions(value: string, position: number): Completions {
function entityCompletions(value: string, position: number): Completions|undefined {
// Look for entity completions
const re = /&[A-Za-z]*;?(?!\d)/g;
let found: RegExpExecArray|null;
let result: Completions;
let result: Completions|undefined = undefined;
while (found = re.exec(value)) {
let len = found[0].length;
if (position >= found.index && position < (found.index + len)) {
@ -240,7 +240,7 @@ function entityCompletions(value: string, position: number): Completions {
return result;
}
function interpolationCompletions(info: TemplateInfo, position: number): Completions {
function interpolationCompletions(info: TemplateInfo, position: number): Completions|undefined {
// Look for an interpolation in at the position.
const templatePath = new TemplateAstPath(info.templateAst, position);
const mostSpecific = templatePath.tail;
@ -258,13 +258,14 @@ function interpolationCompletions(info: TemplateInfo, position: number): Complet
// the attributes of an "a" element, not requesting completion in the a text element. This
// code checks for this case and returns element completions if it is detected or undefined
// if it is not.
function voidElementAttributeCompletions(info: TemplateInfo, path: HtmlAstPath): Completions {
function voidElementAttributeCompletions(info: TemplateInfo, path: HtmlAstPath): Completions|
undefined {
let tail = path.tail;
if (tail instanceof Text) {
let match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/);
let match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/) !;
// The position must be after the match, otherwise we are still in a place where elements
// are expected (such as `<|a` or `<a|`; we only want attributes for `<a |` or after).
if (match && path.position >= match.index + match[0].length + tail.sourceSpan.start.offset) {
if (match && path.position >= match.index ! + match[0].length + tail.sourceSpan.start.offset) {
return attributeCompletionsForElement(info, match[3]);
}
}
@ -310,7 +311,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
this.info.expressionParser.parseTemplateBindings(key, this.attr.value, null);
// find the template binding that contains the position
const valueRelativePosition = this.position - this.attr.valueSpan.start.offset - 1;
const valueRelativePosition = this.position - this.attr.valueSpan !.start.offset - 1;
const bindings = templateBindingResult.templateBindings;
const binding =
bindings.find(
@ -340,7 +341,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
// template reference's type parameter.
const directiveMetadata = selectorInfo.map.get(selector);
const contextTable =
this.info.template.query.getTemplateContext(directiveMetadata.type.reference);
this.info.template.query.getTemplateContext(directiveMetadata !.type.reference);
if (contextTable) {
this.result = this.symbolsToCompletions(contextTable.values());
}
@ -370,7 +371,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
const expressionPosition = this.position - ast.sourceSpan.start.offset;
if (inSpan(expressionPosition, ast.value.span)) {
const completions = getExpressionCompletions(
this.getExpressionScope(), ast.value, expressionPosition, this.info.template.query);
this.getExpressionScope !(), ast.value, expressionPosition, this.info.template.query);
if (completions) {
this.result = this.symbolsToCompletions(completions);
}
@ -379,8 +380,8 @@ class ExpressionVisitor extends NullTemplateVisitor {
private attributeValueCompletions(value: AST, position?: number) {
const symbols = getExpressionCompletions(
this.getExpressionScope(), value, position == null ? this.attributeValuePosition : position,
this.info.template.query);
this.getExpressionScope !(), value,
position == null ? this.attributeValuePosition : position, this.info.template.query);
if (symbols) {
this.result = this.symbolsToCompletions(symbols);
}
@ -392,7 +393,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
}
private get attributeValuePosition() {
return this.position - this.attr.valueSpan.start.offset - 1;
return this.position - this.attr !.valueSpan !.start.offset - 1;
}
}

View File

@ -29,7 +29,7 @@ export function getTemplateDiagnostics(
results.push(...ast.parseErrors.map<Diagnostic>(
e => ({
kind: DiagnosticKind.Error,
span: offsetSpan(spanOf(e.span), template.span.start),
span: offsetSpan(spanOf(e.span) !, template.span.start),
message: e.msg
})));
} else if (ast.templateAst) {
@ -66,7 +66,8 @@ export function getDeclarationDiagnostics(
report(
`Component '${declaration.type.name}' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration`);
}
if (!declaration.metadata.template.template && !declaration.metadata.template.templateUrl) {
if (!declaration.metadata.template !.template &&
!declaration.metadata.template !.templateUrl) {
report(`Component ${declaration.type.name} must have a template or templateUrl`);
}
} else {
@ -74,7 +75,7 @@ export function getDeclarationDiagnostics(
directives = new Set();
modules.ngModules.forEach(module => {
module.declaredDirectives.forEach(
directive => { directives.add(directive.reference); });
directive => { directives !.add(directive.reference); });
});
}
if (!directives.has(declaration.type)) {
@ -92,17 +93,17 @@ function getTemplateExpressionDiagnostics(
template: TemplateSource, astResult: AstResult): Diagnostics {
const info: TemplateInfo = {
template,
htmlAst: astResult.htmlAst,
directive: astResult.directive,
directives: astResult.directives,
pipes: astResult.pipes,
templateAst: astResult.templateAst,
expressionParser: astResult.expressionParser
htmlAst: astResult.htmlAst !,
directive: astResult.directive !,
directives: astResult.directives !,
pipes: astResult.pipes !,
templateAst: astResult.templateAst !,
expressionParser: astResult.expressionParser !
};
const visitor = new ExpressionDiagnosticsVisitor(
info, (path: TemplateAstPath, includeEvent: boolean) =>
getExpressionScope(info, path, includeEvent));
templateVisitAll(visitor, astResult.templateAst);
templateVisitAll(visitor, astResult.templateAst !);
return visitor.diagnostics;
}
@ -153,15 +154,15 @@ class ExpressionDiagnosticsVisitor extends TemplateAstChildVisitor {
visitVariable(ast: VariableAst): void {
const directive = this.directiveSummary;
if (directive && ast.value) {
const context = this.info.template.query.getTemplateContext(directive.type.reference);
const context = this.info.template.query.getTemplateContext(directive.type.reference) !;
if (context && !context.has(ast.value)) {
if (ast.value === '$implicit') {
this.reportError(
'The template context does not have an implicit value', spanOf(ast.sourceSpan));
'The template context does not have an implicit value', spanOf(ast.sourceSpan) !);
} else {
this.reportError(
`The template context does not defined a member called '${ast.value}'`,
spanOf(ast.sourceSpan));
spanOf(ast.sourceSpan) !);
}
}
}
@ -180,7 +181,7 @@ class ExpressionDiagnosticsVisitor extends TemplateAstChildVisitor {
// Find directive that refernces this template
this.directiveSummary =
ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type));
ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type)) !;
// Process children
super.visitEmbeddedTemplate(ast, context);
@ -225,7 +226,7 @@ class ExpressionDiagnosticsVisitor extends TemplateAstChildVisitor {
return result;
}
private findElement(position: number): Element {
private findElement(position: number): Element|undefined {
const htmlPath = new HtmlAstPath(this.info.htmlAst, position);
if (htmlPath.tail instanceof Element) {
return htmlPath.tail;

View File

@ -25,10 +25,10 @@ export function getExpressionDiagnostics(
}
export function getExpressionCompletions(
scope: SymbolTable, ast: AST, position: number, query: SymbolQuery): Symbol[] {
scope: SymbolTable, ast: AST, position: number, query: SymbolQuery): Symbol[]|undefined {
const path = new AstPath(ast, position);
if (path.empty) return undefined;
const tail = path.tail;
const tail = path.tail !;
let result: SymbolTable|undefined = scope;
function getType(ast: AST): Symbol { return new AstType(scope, query, {}).getType(ast); }
@ -84,15 +84,15 @@ export function getExpressionCompletions(
export function getExpressionSymbol(
scope: SymbolTable, ast: AST, position: number,
query: SymbolQuery): {symbol: Symbol, span: Span} {
query: SymbolQuery): {symbol: Symbol, span: Span}|undefined {
const path = new AstPath(ast, position, /* excludeEmpty */ true);
if (path.empty) return undefined;
const tail = path.tail;
const tail = path.tail !;
function getType(ast: AST): Symbol { return new AstType(scope, query, {}).getType(ast); }
let symbol: Symbol = undefined;
let span: Span = undefined;
let symbol: Symbol|undefined = undefined;
let span: Span|undefined = undefined;
// If the completion request is in a not in a pipe or property access then the global scope
// (that is the scope of the implicit receiver) is the right scope as the user is typing the
@ -340,7 +340,7 @@ class AstType implements ExpressionVisitor {
// support contextual typing of arguments so this is simpler than TypeScript's
// version.
const args = ast.args.map(arg => this.getType(arg));
const target = this.getType(ast.target);
const target = this.getType(ast.target !);
if (!target || !target.callable) return this.reportError('Call target is not callable', ast);
const signature = target.selectSignature(args);
if (signature) return signature.result;
@ -434,7 +434,7 @@ class AstType implements ExpressionVisitor {
// The type of a pipe node is the return type of the pipe's transform method. The table returned
// by getPipes() is expected to contain symbols with the corresponding transform method type.
const pipe = this.query.getPipes().get(ast.name);
if (!pipe) return this.reportError(`No pipe by the name ${pipe.name} found`, ast);
if (!pipe) return this.reportError(`No pipe by the name ${ast.name} found`, ast);
const expType = this.getType(ast.exp);
const signature =
pipe.selectSignature([expType].concat(ast.args.map(arg => this.getType(arg))));
@ -495,8 +495,8 @@ class AstType implements ExpressionVisitor {
// The type of a method is the selected methods result type.
const method = receiverType.members().get(ast.name);
if (!method) return this.reportError(`Unknown method ${ast.name}`, ast);
if (!method.type.callable) return this.reportError(`Member ${ast.name} is not callable`, ast);
const signature = method.type.selectSignature(ast.args.map(arg => this.getType(arg)));
if (!method.type !.callable) return this.reportError(`Member ${ast.name} is not callable`, ast);
const signature = method.type !.selectSignature(ast.args.map(arg => this.getType(arg)));
if (!signature)
return this.reportError(`Unable to resolve signature for call of method ${ast.name}`, ast);
return signature.result;
@ -550,7 +550,7 @@ class AstType implements ExpressionVisitor {
private isAny(symbol: Symbol): boolean {
return !symbol || this.query.getTypeKind(symbol) == BuiltinType.Any ||
(symbol.type && this.isAny(symbol.type));
(!!symbol.type && this.isAny(symbol.type));
}
}
@ -601,7 +601,7 @@ function visitChildren(ast: AST, visitor: ExpressionVisitor) {
visit(ast.falseExp);
},
visitFunctionCall(ast) {
visit(ast.target);
visit(ast.target !);
visitAll(ast.args);
},
visitImplicitReceiver(ast) {},
@ -676,7 +676,7 @@ function getReferences(info: TemplateInfo): SymbolDeclaration[] {
function processReferences(references: ReferenceAst[]) {
for (const reference of references) {
let type: Symbol;
let type: Symbol = undefined !;
if (reference.value) {
type = info.template.query.getTypeSymbol(tokenReference(reference.value));
}
@ -721,11 +721,11 @@ function getVarDeclarations(info: TemplateInfo, path: TemplateAstPath): SymbolDe
.find(c => !!c);
// Determine the type of the context field referenced by variable.value.
let type: Symbol;
let type: Symbol = undefined !;
if (context) {
const value = context.get(variable.value);
if (value) {
type = value.type;
type = value.type !;
let kind = info.template.query.getTypeKind(type);
if (kind === BuiltinType.Any || kind == BuiltinType.Unbound) {
// The any type is not very useful here. For special cases, such as ngFor, we can do
@ -762,7 +762,7 @@ function refinedVariableType(
const bindingType =
new AstType(info.template.members, info.template.query, {}).getType(ngForOfBinding.value);
if (bindingType) {
return info.template.query.getElementType(bindingType);
return info.template.query.getElementType(bindingType) !;
}
}
}
@ -771,7 +771,7 @@ function refinedVariableType(
return type;
}
function getDefintionOf(info: TemplateInfo, ast: TemplateAst): Definition {
function getDefintionOf(info: TemplateInfo, ast: TemplateAst): Definition|undefined {
if (info.fileName) {
const templateOffset = info.template.span.start;
return [{

View File

@ -10,7 +10,7 @@ import {TemplateInfo} from './common';
import {locateSymbol} from './locate_symbol';
import {Hover, HoverTextSection, Symbol} from './types';
export function getHover(info: TemplateInfo): Hover {
export function getHover(info: TemplateInfo): Hover|undefined {
const result = locateSymbol(info);
if (result) {
return {text: hoverTextOf(result.symbol), span: result.span};

View File

@ -59,7 +59,7 @@ class HtmlAstPathBuilder extends ChildVisitor {
constructor(private position: number) { super(); }
visit(ast: Node, context: any): any {
let span = spanOf(ast);
let span = spanOf(ast as any);
if (inSpan(this.position, span)) {
this.path.push(ast);
} else {

View File

@ -32,7 +32,7 @@ class LanguageServiceImpl implements LanguageService {
getTemplateReferences(): string[] { return this.host.getTemplateReferences(); }
getDiagnostics(fileName: string): Diagnostics {
getDiagnostics(fileName: string): Diagnostics|undefined {
let results: Diagnostics = [];
let templates = this.host.getTemplates(fileName);
if (templates && templates.length) {
@ -70,14 +70,14 @@ class LanguageServiceImpl implements LanguageService {
}
}
getHoverAt(fileName: string, position: number): Hover {
getHoverAt(fileName: string, position: number): Hover|undefined {
let templateInfo = this.getTemplateAstAtPosition(fileName, position);
if (templateInfo) {
return getHover(templateInfo);
}
}
private getTemplateAstAtPosition(fileName: string, position: number): TemplateInfo {
private getTemplateAstAtPosition(fileName: string, position: number): TemplateInfo|undefined {
let template = this.host.getTemplateAt(fileName, position);
if (template) {
let astResult = this.getTemplateAst(template, fileName);
@ -87,18 +87,18 @@ class LanguageServiceImpl implements LanguageService {
fileName,
template,
htmlAst: astResult.htmlAst,
directive: astResult.directive,
directives: astResult.directives,
pipes: astResult.pipes,
directive: astResult.directive !,
directives: astResult.directives !,
pipes: astResult.pipes !,
templateAst: astResult.templateAst,
expressionParser: astResult.expressionParser
expressionParser: astResult.expressionParser !
};
}
return undefined;
}
getTemplateAst(template: TemplateSource, contextFile: string): AstResult {
let result: AstResult;
let result: AstResult = undefined !;
try {
const resolvedMetadata =
this.metadataResolver.getNonNormalizedDirectiveMetadata(template.type as any);
@ -109,10 +109,10 @@ class LanguageServiceImpl implements LanguageService {
const expressionParser = new Parser(new Lexer());
const config = new CompilerConfig();
const parser = new TemplateParser(
config, expressionParser, new DomElementSchemaRegistry(), htmlParser, null, []);
config, expressionParser, new DomElementSchemaRegistry(), htmlParser, null !, []);
const htmlResult = htmlParser.parse(template.source, '', true);
const analyzedModules = this.host.getAnalyzedModules();
let errors: Diagnostic[] = undefined;
let errors: Diagnostic[] = undefined !;
let ngModule = analyzedModules.ngModuleByPipeOrDirective.get(template.type);
if (!ngModule) {
// Reported by the the declaration diagnostics.
@ -122,7 +122,7 @@ class LanguageServiceImpl implements LanguageService {
const resolvedDirectives = ngModule.transitiveModule.directives.map(
d => this.host.resolver.getNonNormalizedDirectiveMetadata(d.reference));
const directives =
resolvedDirectives.filter(d => d !== null).map(d => d.metadata.toSummary());
resolvedDirectives.filter(d => d !== null).map(d => d !.metadata.toSummary());
const pipes = ngModule.transitiveModule.pipes.map(
p => this.host.resolver.getOrLoadPipeMetadata(p.reference).toSummary());
const schemas = ngModule.schemas;
@ -171,7 +171,7 @@ function uniqueBySpan < T extends {
}
function findSuitableDefaultModule(modules: NgAnalyzedModules): CompileNgModuleMetadata {
let result: CompileNgModuleMetadata;
let result: CompileNgModuleMetadata = undefined !;
let resultSize = 0;
for (const module of modules.ngModules) {
const moduleSize = module.transitiveModule.directives.length;

View File

@ -20,18 +20,18 @@ export interface SymbolInfo {
span: Span;
}
export function locateSymbol(info: TemplateInfo): SymbolInfo {
const templatePosition = info.position - info.template.span.start;
export function locateSymbol(info: TemplateInfo): SymbolInfo|undefined {
const templatePosition = info.position ! - info.template.span.start;
const path = new TemplateAstPath(info.templateAst, templatePosition);
if (path.tail) {
let symbol: Symbol = undefined;
let span: Span = undefined;
let symbol: Symbol = undefined !;
let span: Span = undefined !;
const attributeValueSymbol = (ast: AST, inEvent: boolean = false): boolean => {
const attribute = findAttribute(info);
if (attribute) {
if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
const scope = getExpressionScope(info, path, inEvent);
const expressionOffset = attribute.valueSpan.start.offset + 1;
const expressionOffset = attribute.valueSpan !.start.offset + 1;
const result = getExpressionSymbol(
scope, ast, templatePosition - expressionOffset, info.template.query);
if (result) {
@ -52,28 +52,28 @@ export function locateSymbol(info: TemplateInfo): SymbolInfo {
if (component) {
symbol = info.template.query.getTypeSymbol(component.directive.type.reference);
symbol = symbol && new OverrideKindSymbol(symbol, 'component');
span = spanOf(ast);
span = spanOf(ast) !;
} else {
// Find a directive that matches the element name
const directive =
ast.directives.find(d => d.directive.selector.indexOf(ast.name) >= 0);
ast.directives.find(d => d.directive.selector !.indexOf(ast.name) >= 0);
if (directive) {
symbol = info.template.query.getTypeSymbol(directive.directive.type.reference);
symbol = symbol && new OverrideKindSymbol(symbol, 'directive');
span = spanOf(ast);
span = spanOf(ast) !;
}
}
},
visitReference(ast) {
symbol = info.template.query.getTypeSymbol(tokenReference(ast.value));
span = spanOf(ast);
span = spanOf(ast) !;
},
visitVariable(ast) {},
visitEvent(ast) {
if (!attributeValueSymbol(ast.handler, /* inEvent */ true)) {
symbol = findOutputBinding(info, path, ast);
symbol = findOutputBinding(info, path, ast) !;
symbol = symbol && new OverrideKindSymbol(symbol, 'event');
span = spanOf(ast);
span = spanOf(ast) !;
}
},
visitElementProperty(ast) { attributeValueSymbol(ast.value); },
@ -93,12 +93,12 @@ export function locateSymbol(info: TemplateInfo): SymbolInfo {
visitText(ast) {},
visitDirective(ast) {
symbol = info.template.query.getTypeSymbol(ast.directive.type.reference);
span = spanOf(ast);
span = spanOf(ast) !;
},
visitDirectiveProperty(ast) {
if (!attributeValueSymbol(ast.value)) {
symbol = findInputBinding(info, path, ast);
span = spanOf(ast);
symbol = findInputBinding(info, path, ast) !;
span = spanOf(ast) !;
}
}
},
@ -109,14 +109,15 @@ export function locateSymbol(info: TemplateInfo): SymbolInfo {
}
}
function findAttribute(info: TemplateInfo): Attribute {
const templatePosition = info.position - info.template.span.start;
function findAttribute(info: TemplateInfo): Attribute|undefined {
const templatePosition = info.position ! - info.template.span.start;
const path = new HtmlAstPath(info.htmlAst, templatePosition);
return path.first(Attribute);
}
function findInputBinding(
info: TemplateInfo, path: TemplateAstPath, binding: BoundDirectivePropertyAst): Symbol {
info: TemplateInfo, path: TemplateAstPath, binding: BoundDirectivePropertyAst): Symbol|
undefined {
const element = path.first(ElementAst);
if (element) {
for (const directive of element.directives) {
@ -133,7 +134,7 @@ function findInputBinding(
}
function findOutputBinding(
info: TemplateInfo, path: TemplateAstPath, binding: BoundEventAst): Symbol {
info: TemplateInfo, path: TemplateAstPath, binding: BoundEventAst): Symbol|undefined {
const element = path.first(ElementAst);
if (element) {
for (const directive of element.directives) {

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
constructor(private host: ts.LanguageServiceHost) {
if (host.directoryExists)
this.directoryExists = directoryName => this.host.directoryExists(directoryName);
this.directoryExists = directoryName => this.host.directoryExists !(directoryName);
}
fileExists(fileName: string): boolean { return !!this.host.getScriptSnapshot(fileName); }
@ -22,6 +22,7 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost {
if (snapshot) {
return snapshot.getText(0, snapshot.getLength());
}
return undefined !;
}
directoryExists: (directoryName: string) => boolean;
@ -32,7 +33,7 @@ export class ReflectorHost extends CompilerHost {
private getProgram: () => ts.Program, serviceHost: ts.LanguageServiceHost,
options: AngularCompilerOptions) {
super(
null, options,
null !, options,
new ModuleResolutionHostAdapter(new ReflectorModuleModuleResolutionHost(serviceHost)),
{verboseInvalidExpression: true});
}

View File

@ -104,10 +104,10 @@ class TemplateAstPathBuilder extends TemplateAstChildVisitor {
constructor(private position: number, private allowWidening: boolean) { super(); }
visit(ast: TemplateAst, context: any): any {
let span = spanOf(ast);
let span = spanOf(ast) !;
if (inSpan(this.position, span)) {
const len = this.path.length;
if (!len || this.allowWidening || isNarrower(span, spanOf(this.path[len - 1]))) {
if (!len || this.allowWidening || isNarrower(span, spanOf(this.path[len - 1]) !)) {
this.path.push(ast);
}
} else {

View File

@ -45,7 +45,7 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
}
const serviceHost = new TypeScriptServiceHost(info.languageServiceHost, info.languageService);
const ls = createLanguageService(serviceHost);
const ls = createLanguageService(serviceHost as any);
serviceHost.setSite(ls);
proxy.getCompletionsAtPosition = function(fileName: string, position: number) {
@ -76,7 +76,7 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
if (ours) {
const displayParts: typeof base.displayParts = [];
for (const part of ours.text) {
displayParts.push({kind: part.language, text: part.text});
displayParts.push({kind: part.language !, text: part.text});
}
base = {
displayParts,

View File

@ -84,7 +84,7 @@ export interface TemplateSource {
*
* @experimental
*/
export type TemplateSources = TemplateSource[] /* | undefined */;
export type TemplateSources = TemplateSource[] | undefined;
/**
* Error information found getting declaration information
@ -227,7 +227,7 @@ export interface Symbol {
/**
* A symbol representing type of the symbol.
*/
readonly type: Symbol /* | undefined */;
readonly type: Symbol|undefined;
/**
@ -235,7 +235,7 @@ export interface Symbol {
* is the class or interface of the method. If no container is appropriate, undefined is
* returned.
*/
readonly container: Symbol /* | undefined */;
readonly container: Symbol|undefined;
/**
* The symbol is public in the container.
@ -250,7 +250,7 @@ export interface Symbol {
/**
* The location of the definition of the symbol
*/
readonly definition: Definition;
readonly definition: Definition|undefined;
/**
* A table of the members of the symbol; that is, the members that can appear
@ -270,13 +270,13 @@ export interface Symbol {
* given the `types` supplied. If no signature would match, this method should
* return `undefined`.
*/
selectSignature(types: Symbol[]): Signature /* | undefined */;
selectSignature(types: Symbol[]): Signature|undefined;
/**
* Return the type of the expression if this symbol is indexed by `argument`.
* If the symbol cannot be indexed, this method should return `undefined`.
*/
indexed(argument: Symbol): Symbol /* | undefined */;
indexed(argument: Symbol): Symbol|undefined;
}
/**
@ -296,7 +296,7 @@ export interface SymbolTable {
* Get the symbol corresponding to `key` or `undefined` if there is no symbol in the
* table by the name `key`.
*/
get(key: string): Symbol /* | undefined */;
get(key: string): Symbol|undefined;
/**
* Returns `true` if the table contains a `Symbol` with the name `key`.
@ -364,7 +364,7 @@ export interface SymbolQuery {
* Return element type symbol for an array type if the `type` is an array type. Otherwise return
* undefined.
*/
getElementType(type: Symbol): Symbol /* | undefined */;
getElementType(type: Symbol): Symbol|undefined;
/**
* Return a type that is the non-nullable version of the given type. If `type` is already
@ -385,7 +385,7 @@ export interface SymbolQuery {
/**
* Return the members that are in the context of a type's template reference.
*/
getTemplateContext(type: StaticSymbol): SymbolTable;
getTemplateContext(type: StaticSymbol): SymbolTable|undefined;
/**
* Produce a symbol table with the given symbols. Used to produce a symbol table
@ -406,7 +406,7 @@ export interface SymbolQuery {
/**
* Return the span of the narrowest non-token node at the given location.
*/
getSpanAt(line: number, column: number): Span /* | undefined */;
getSpanAt(line: number, column: number): Span|undefined;
}
/**
@ -519,7 +519,7 @@ export interface Completion {
*
* @experimental
*/
export type Completions = Completion[] /* | undefined */;
export type Completions = Completion[] | undefined;
/**
* A file and span.
@ -532,7 +532,7 @@ export interface Location {
/**
* A defnition location(s).
*/
export type Definition = Location[] /* | undefined */;
export type Definition = Location[] | undefined;
/**
* The kind of diagnostic message.
@ -597,7 +597,7 @@ export interface PipeInfo {
*
* @experimental
*/
export type Pipes = PipeInfo[] /* | undefined */;
export type Pipes = PipeInfo[] | undefined;
/**
* Describes a symbol to type binding used to build a symbol table.
@ -699,30 +699,30 @@ export interface LanguageService {
/**
* Returns a list of all the external templates referenced by the project.
*/
getTemplateReferences(): string[] /* | undefined */;
getTemplateReferences(): string[]|undefined;
/**
* Returns a list of all error for all templates in the given file.
*/
getDiagnostics(fileName: string): Diagnostics /* | undefined */;
getDiagnostics(fileName: string): Diagnostics|undefined;
/**
* Return the completions at the given position.
*/
getCompletionsAt(fileName: string, position: number): Completions /* | undefined */;
getCompletionsAt(fileName: string, position: number): Completions|undefined;
/**
* Return the definition location for the symbol at position.
*/
getDefinitionAt(fileName: string, position: number): Definition /* | undefined */;
getDefinitionAt(fileName: string, position: number): Definition|undefined;
/**
* Return the hover information for the symbol at position.
*/
getHoverAt(fileName: string, position: number): Hover /* | undefined */;
getHoverAt(fileName: string, position: number): Hover|undefined;
/**
* Return the pipes that are available at the given position.
*/
getPipesAt(fileName: string, position: number): Pipes /* | undefined */;
getPipesAt(fileName: string, position: number): Pipes|undefined;
}

View File

@ -128,14 +128,14 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
return this.templateReferences;
}
getTemplateAt(fileName: string, position: number): TemplateSource|undefined {
getTemplateAt(fileName: string, position: number): TemplateSource {
let sourceFile = this.getSourceFile(fileName);
if (sourceFile) {
this.context = sourceFile.fileName;
let node = this.findNode(sourceFile, position);
if (node) {
return this.getSourceFromNode(
fileName, this.host.getScriptVersion(sourceFile.fileName), node);
fileName, this.host.getScriptVersion(sourceFile.fileName), node) !;
}
} else {
this.ensureTemplateMap();
@ -143,9 +143,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
const componentType = this.fileToComponent.get(fileName);
if (componentType) {
return this.getSourceFromType(
fileName, this.host.getScriptVersion(fileName), componentType);
fileName, this.host.getScriptVersion(fileName), componentType) !;
}
}
return null !;
}
getAnalyzedModules(): NgAnalyzedModules {
@ -222,10 +223,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
updateAnalyzedModules() {
this.validate();
if (this.modulesOutOfDate) {
this.analyzedModules = null;
this._reflector = null;
this.templateReferences = null;
this.fileToComponent = null;
this.analyzedModules = null !;
this._reflector = null !;
this.templateReferences = null !;
this.fileToComponent = null !;
this.ensureAnalyzedModules();
this.modulesOutOfDate = false;
}
@ -270,10 +271,10 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
}
private clearCaches() {
this._checker = null;
this._checker = null !;
this._typeCache = [];
this._resolver = null;
this.collectedErrors = null;
this._resolver = null !;
this.collectedErrors = null !;
this.modulesOutOfDate = true;
}
@ -286,7 +287,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
for (const module of ngModuleSummary.ngModules) {
for (const directive of module.declaredDirectives) {
const {metadata, annotation} =
this.resolver.getNonNormalizedDirectiveMetadata(directive.reference);
this.resolver.getNonNormalizedDirectiveMetadata(directive.reference) !;
if (metadata.isComponent && metadata.template && metadata.template.templateUrl) {
const templateName = urlResolver.resolve(
componentModuleUrl(this.reflector, directive.reference, annotation),
@ -343,7 +344,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (declaration && declaration.name) {
const sourceFile = this.getSourceFile(fileName);
return this.getSourceFromDeclaration(
fileName, version, this.stringOf(node), shrink(spanOf(node)),
fileName, version, this.stringOf(node) !, shrink(spanOf(node)),
this.reflector.getStaticSymbol(sourceFile.fileName, declaration.name.text),
declaration, node, sourceFile);
}
@ -357,7 +358,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
let result: TemplateSource|undefined = undefined;
const declaration = this.getTemplateClassFromStaticSymbol(type);
if (declaration) {
const snapshot = this.host.getScriptSnapshot(fileName);
const snapshot = this.host.getScriptSnapshot(fileName) !;
const source = snapshot.getText(0, snapshot.getLength());
result = this.getSourceFromDeclaration(
fileName, version, source, {start: 0, end: source.length}, type, declaration, declaration,
@ -410,14 +411,14 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (!result) {
this._summaryResolver = new AotSummaryResolver(
{
loadSummary(filePath: string) { return null; },
isSourceFile(sourceFilePath: string) { return true; },
getOutputFileName(sourceFilePath: string) { return null; }
loadSummary(filePath: string) { return null !; },
isSourceFile(sourceFilePath: string) { return true !; },
getOutputFileName(sourceFilePath: string) { return null !; }
},
this._staticSymbolCache);
result = this._staticSymbolResolver = new StaticSymbolResolver(
this.reflectorHost, this._staticSymbolCache, this._summaryResolver,
(e, filePath) => this.collectError(e, filePath));
this.reflectorHost as any, this._staticSymbolCache, this._summaryResolver,
(e, filePath) => this.collectError(e, filePath !));
}
return result;
}
@ -427,7 +428,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
if (!result) {
const ssr = this.staticSymbolResolver;
result = this._reflector = new StaticReflector(
this._summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath));
this._summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath !));
StaticAndDynamicReflectionCapabilities.install(result);
}
return result;
@ -439,7 +440,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
const declarationNode = ts.forEachChild(source, child => {
if (child.kind === ts.SyntaxKind.ClassDeclaration) {
const classDeclaration = child as ts.ClassDeclaration;
if (classDeclaration.name.text === type.name) {
if (classDeclaration.name !.text === type.name) {
return classDeclaration;
}
}
@ -450,14 +451,15 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
return undefined;
}
private static missingTemplate = <[ts.ClassDeclaration, ts.Expression]>[];
private static missingTemplate: [ts.ClassDeclaration | undefined, ts.Expression|undefined] =
[undefined, undefined];
/**
* Given a template string node, see if it is an Angular template string, and if so return the
* containing class.
*/
private getTemplateClassDeclFromNode(currentToken: ts.Node):
[ts.ClassDeclaration, ts.Expression] {
[ts.ClassDeclaration | undefined, ts.Expression|undefined] {
// Verify we are in a 'template' property assignment, in an object literal, which is an call
// arg, in a decorator
let parentNode = currentToken.parent; // PropertyAssignment
@ -519,7 +521,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
try {
if (this.resolver.isDirective(staticSymbol as any)) {
const {metadata} =
this.resolver.getNonNormalizedDirectiveMetadata(staticSymbol as any);
this.resolver.getNonNormalizedDirectiveMetadata(staticSymbol as any) !;
const declarationSpan = spanOf(target);
return {
type: staticSymbol,
@ -609,7 +611,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
private program: ts.Program, private checker: ts.TypeChecker, private source: ts.SourceFile,
private fetchPipes: () => SymbolTable) {}
getTypeKind(symbol: Symbol): BuiltinType { return typeKindOf(this.getTsTypeOf(symbol)); }
getTypeKind(symbol: Symbol): BuiltinType { return typeKindOf(this.getTsTypeOf(symbol) !); }
getBuiltinType(kind: BuiltinType): Symbol {
// TODO: Replace with typeChecker API when available.
@ -637,7 +639,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
return this.getBuiltinType(BuiltinType.Any);
}
getElementType(type: Symbol): Symbol {
getElementType(type: Symbol): Symbol|undefined {
if (type instanceof TypeWrapper) {
const elementType = getTypeParameterOf(type.tsType, 'Array');
if (elementType) {
@ -659,7 +661,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
return result;
}
getTemplateContext(type: StaticSymbol): SymbolTable {
getTemplateContext(type: StaticSymbol): SymbolTable|undefined {
const context: TypeContext = {node: this.source, program: this.program, checker: this.checker};
const typeSymbol = findClassSymbolInContext(type, context);
if (typeSymbol) {
@ -670,7 +672,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
getTypeSymbol(type: StaticSymbol): Symbol {
const context: TypeContext = {node: this.source, program: this.program, checker: this.checker};
const typeSymbol = findClassSymbolInContext(type, context);
const typeSymbol = findClassSymbolInContext(type, context) !;
return new SymbolWrapper(typeSymbol, context);
}
@ -688,15 +690,17 @@ class TypeScriptSymbolQuery implements SymbolQuery {
return result;
}
getSpanAt(line: number, column: number): Span { return spanAt(this.source, line, column); }
getSpanAt(line: number, column: number): Span|undefined {
return spanAt(this.source, line, column);
}
private getTemplateRefContextType(type: ts.Symbol): ts.Symbol {
const constructor = type.members && type.members['__constructor'];
private getTemplateRefContextType(type: ts.Symbol): ts.Symbol|undefined {
const constructor = type.members && type.members !['__constructor'];
if (constructor) {
const constructorDeclaration = constructor.declarations[0] as ts.ConstructorTypeNode;
const constructorDeclaration = constructor.declarations ![0] as ts.ConstructorTypeNode;
for (const parameter of constructorDeclaration.parameters) {
const type = this.checker.getTypeAtLocation(parameter.type);
if (type.symbol.name == 'TemplateRef' && isReferenceType(type)) {
const type = this.checker.getTypeAtLocation(parameter.type !);
if (type.symbol !.name == 'TemplateRef' && isReferenceType(type)) {
const typeReference = type as ts.TypeReference;
if (typeReference.typeArguments.length === 1) {
return typeReference.typeArguments[0].symbol;
@ -706,7 +710,7 @@ class TypeScriptSymbolQuery implements SymbolQuery {
}
}
private getTsTypeOf(symbol: Symbol): ts.Type {
private getTsTypeOf(symbol: Symbol): ts.Type|undefined {
const type = this.getTypeWrapper(symbol);
return type && type.tsType;
}
@ -838,7 +842,7 @@ class SymbolWrapper implements Symbol {
const typeWrapper = new TypeWrapper(declaredType, this.context);
this._members = typeWrapper.members();
} else {
this._members = new SymbolTableWrapper(this.symbol.members, this.context);
this._members = new SymbolTableWrapper(this.symbol.members !, this.context);
}
}
return this._members;
@ -949,7 +953,7 @@ class MapSymbolTable implements SymbolTable {
add(symbol: Symbol) {
if (this.map.has(symbol.name)) {
const previous = this.map.get(symbol.name);
const previous = this.map.get(symbol.name) !;
this._values[this._values.indexOf(previous)] = symbol;
}
this.map.set(symbol.name, symbol);
@ -973,18 +977,18 @@ class MapSymbolTable implements SymbolTable {
class PipesTable implements SymbolTable {
constructor(private pipes: Pipes, private context: TypeContext) {}
get size() { return this.pipes.length; }
get size() { return this.pipes !.length; }
get(key: string): Symbol {
const pipe = this.pipes.find(pipe => pipe.name == key);
get(key: string): Symbol|undefined {
const pipe = this.pipes !.find(pipe => pipe.name == key);
if (pipe) {
return new PipeSymbol(pipe, this.context);
}
}
has(key: string): boolean { return this.pipes.find(pipe => pipe.name == key) != null; }
has(key: string): boolean { return this.pipes !.find(pipe => pipe.name == key) != null; }
values(): Symbol[] { return this.pipes.map(pipe => new PipeSymbol(pipe, this.context)); }
values(): Symbol[] { return this.pipes !.map(pipe => new PipeSymbol(pipe, this.context)); }
}
class PipeSymbol implements Symbol {
@ -1013,11 +1017,11 @@ class PipeSymbol implements Symbol {
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); }
selectSignature(types: Symbol[]): Signature|undefined {
let signature = selectSignature(this.tsType, this.context, types);
let signature = selectSignature(this.tsType, this.context, types) !;
if (types.length == 1) {
const parameterType = types[0];
if (parameterType instanceof TypeWrapper) {
let resultType: ts.Type = undefined;
let resultType: ts.Type|undefined = undefined;
switch (this.name) {
case 'async':
switch (parameterType.name) {
@ -1051,7 +1055,7 @@ class PipeSymbol implements Symbol {
if (!type) {
const classSymbol = this.findClassSymbol(this.pipe.symbol);
if (classSymbol) {
type = this._tsType = this.findTransformMethodType(classSymbol);
type = this._tsType = this.findTransformMethodType(classSymbol) !;
}
if (!type) {
type = this._tsType = getBuiltinTypeFromTs(BuiltinType.Any, this.context);
@ -1060,7 +1064,7 @@ class PipeSymbol implements Symbol {
return type;
}
private findClassSymbol(type: StaticSymbol): ts.Symbol {
private findClassSymbol(type: StaticSymbol): ts.Symbol|undefined {
return findClassSymbolInContext(type, this.context);
}
@ -1075,7 +1079,7 @@ class PipeSymbol implements Symbol {
}
}
function findClassSymbolInContext(type: StaticSymbol, context: TypeContext): ts.Symbol {
function findClassSymbolInContext(type: StaticSymbol, context: TypeContext): ts.Symbol|undefined {
const sourceFile = context.program.getSourceFile(type.filePath);
if (sourceFile) {
const moduleSymbol = (sourceFile as any).module || (sourceFile as any).symbol;
@ -1092,7 +1096,7 @@ class EmptyTable implements SymbolTable {
static instance = new EmptyTable();
}
function findTsConfig(fileName: string): string {
function findTsConfig(fileName: string): string|undefined {
let dir = path.dirname(fileName);
while (fs.existsSync(dir)) {
const candidate = path.join(dir, 'tsconfig.json');
@ -1110,7 +1114,7 @@ function isBindingPattern(node: ts.Node): node is ts.BindingPattern {
function walkUpBindingElementsAndPatterns(node: ts.Node): ts.Node {
while (node && (node.kind === ts.SyntaxKind.BindingElement || isBindingPattern(node))) {
node = node.parent;
node = node.parent !;
}
return node;
@ -1121,12 +1125,12 @@ function getCombinedNodeFlags(node: ts.Node): ts.NodeFlags {
let flags = node.flags;
if (node.kind === ts.SyntaxKind.VariableDeclaration) {
node = node.parent;
node = node.parent !;
}
if (node && node.kind === ts.SyntaxKind.VariableDeclarationList) {
flags |= node.flags;
node = node.parent;
node = node.parent !;
}
if (node && node.kind === ts.SyntaxKind.VariableStatement) {
@ -1137,7 +1141,7 @@ function getCombinedNodeFlags(node: ts.Node): ts.NodeFlags {
}
function isSymbolPrivate(s: ts.Symbol): boolean {
return s.valueDeclaration && isPrivate(s.valueDeclaration);
return !!s.valueDeclaration && isPrivate(s.valueDeclaration);
}
function getBuiltinTypeFromTs(kind: BuiltinType, context: TypeContext): ts.Type {
@ -1200,10 +1204,10 @@ function shrink(span: Span, offset?: number) {
return {start: span.start + offset, end: span.end - offset};
}
function spanAt(sourceFile: ts.SourceFile, line: number, column: number): Span {
function spanAt(sourceFile: ts.SourceFile, line: number, column: number): Span|undefined {
if (line != null && column != null) {
const position = ts.getPositionOfLineAndCharacter(sourceFile, line, column);
const findChild = function findChild(node: ts.Node): ts.Node {
const findChild = function findChild(node: ts.Node): ts.Node | undefined {
if (node.kind > ts.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
const betterNode = ts.forEachChild(node, findChild);
return betterNode || node;
@ -1230,20 +1234,20 @@ function definitionFromTsSymbol(symbol: ts.Symbol): Definition {
}
}
function parentDeclarationOf(node: ts.Node): ts.Node {
function parentDeclarationOf(node: ts.Node): ts.Node|undefined {
while (node) {
switch (node.kind) {
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.InterfaceDeclaration:
return node;
case ts.SyntaxKind.SourceFile:
return null;
return undefined;
}
node = node.parent;
node = node.parent !;
}
}
function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol {
function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol|undefined {
if (symbol.getFlags() & ts.SymbolFlags.ClassMember && symbol.declarations) {
for (const declaration of symbol.declarations) {
const parent = parentDeclarationOf(declaration);
@ -1257,7 +1261,7 @@ function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol {
}
}
function getTypeParameterOf(type: ts.Type, name: string): ts.Type {
function getTypeParameterOf(type: ts.Type, name: string): ts.Type|undefined {
if (type && type.symbol && type.symbol.name == name) {
const typeArguments: ts.Type[] = (type as any).typeArguments;
if (typeArguments && typeArguments.length <= 1) {
@ -1281,10 +1285,10 @@ function typeKindOf(type: ts.Type): BuiltinType {
return BuiltinType.Null;
} else if (type.flags & ts.TypeFlags.Union) {
// If all the constituent types of a union are the same kind, it is also that kind.
let candidate: BuiltinType;
let candidate: BuiltinType = undefined !;
const unionType = type as ts.UnionType;
if (unionType.types.length > 0) {
candidate = typeKindOf(unionType.types[0]);
candidate = typeKindOf(unionType.types[0]) !;
for (const subType of unionType.types) {
if (candidate != typeKindOf(subType)) {
return BuiltinType.Other;

View File

@ -13,7 +13,7 @@ import {Span} from './types';
export interface SpanHolder {
sourceSpan: ParseSourceSpan;
endSourceSpan?: ParseSourceSpan;
endSourceSpan?: ParseSourceSpan|null;
children?: SpanHolder[];
}
@ -21,7 +21,7 @@ export function isParseSourceSpan(value: any): value is ParseSourceSpan {
return value && !!value.start;
}
export function spanOf(span?: SpanHolder | ParseSourceSpan): Span {
export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
if (!span) return undefined;
if (isParseSourceSpan(span)) {
return {start: span.start.offset, end: span.end.offset};
@ -31,7 +31,7 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span {
} else if (span.children && span.children.length) {
return {
start: span.sourceSpan.start.offset,
end: spanOf(span.children[span.children.length - 1]).end
end: spanOf(span.children[span.children.length - 1]) !.end
};
}
return {start: span.sourceSpan.start.offset, end: span.sourceSpan.end.offset};
@ -40,7 +40,7 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span {
export function inSpan(position: number, span?: Span, exclusive?: boolean): boolean {
return span && exclusive ? position >= span.start && position < span.end :
position >= span.start && position <= span.end;
position >= span !.start && position <= span !.end;
}
export function offsetSpan(span: Span, amount: number): Span {
@ -54,7 +54,7 @@ export function isNarrower(spanA: Span, spanB: Span): boolean {
export function hasTemplateReference(type: CompileTypeMetadata): boolean {
if (type.diDeps) {
for (let diDep of type.diDeps) {
if (diDep.token.identifier && identifierName(diDep.token.identifier) == 'TemplateRef')
if (diDep.token !.identifier && identifierName(diDep.token !.identifier !) == 'TemplateRef')
return true;
}
}
@ -63,8 +63,8 @@ export function hasTemplateReference(type: CompileTypeMetadata): boolean {
export function getSelectors(info: TemplateInfo): SelectorInfo {
const map = new Map<CssSelector, CompileDirectiveSummary>();
const selectors = flatten(info.directives.map(directive => {
const selectors = CssSelector.parse(directive.selector);
const selectors: CssSelector[] = flatten(info.directives.map(directive => {
const selectors: CssSelector[] = CssSelector.parse(directive.selector !);
selectors.forEach(selector => map.set(selector, directive));
return selectors;
}));

View File

@ -48,7 +48,7 @@ describe('completions', () => {
const fileName = '/app/test.ng';
mockHost.override(fileName, ' > {{tle<\n {{retl ><bel/beled}}di>\n la</b </d &a ');
expect(() => ngService.getCompletionsAt(fileName, 31)).not.toThrow();
mockHost.override(fileName, undefined);
mockHost.override(fileName, undefined !);
});
it('should be able to infer the type of a ngForOf', () => {
@ -101,7 +101,7 @@ describe('completions', () => {
}
}
try {
const originalContent = mockHost.getFileContent(fileName);
const originalContent = mockHost.getFileContent(fileName) !;
// For each character in the file, add it to the file and request a completion after it.
for (let index = 0, len = originalContent.length; index < len; index++) {
@ -133,7 +133,7 @@ describe('completions', () => {
tryCompletionsAt(position);
});
} finally {
mockHost.override(fileName, undefined);
mockHost.override(fileName, undefined !);
}
}).not.toThrow();
});
@ -169,12 +169,12 @@ export class MyComponent {
try {
cb(fileName, newContent);
} finally {
mockHost.override(fileName, undefined);
mockHost.override(fileName, undefined !);
}
}
function contains(fileName: string, locationMarker: string, ...names: string[]) {
let location = mockHost.getMarkerLocations(fileName)[locationMarker];
let location = mockHost.getMarkerLocations(fileName) ![locationMarker];
if (location == null) {
throw new Error(`No marker ${locationMarker} found.`);
}

View File

@ -70,7 +70,7 @@ describe('definitions', () => {
function localReference(code: string) {
addCode(code, fileName => {
const refResult = mockHost.getReferenceMarkers(fileName);
const refResult = mockHost.getReferenceMarkers(fileName) !;
for (const name in refResult.references) {
const references = refResult.references[name];
const definitions = refResult.definitions[name];
@ -100,14 +100,14 @@ describe('definitions', () => {
const definition: string = p2 ? p1 : undefined;
let span: Span = p2 && p1.start != null ? p1 : undefined;
if (definition && !span) {
const referencedFileMarkers = mockHost.getReferenceMarkers(referencedFile);
const referencedFileMarkers = mockHost.getReferenceMarkers(referencedFile) !;
expect(referencedFileMarkers).toBeDefined(); // If this fails the test data is wrong.
const spans = referencedFileMarkers.definitions[definition];
expect(spans).toBeDefined(); // If this fails the test data is wrong.
span = spans[0];
}
addCode(code, fileName => {
const refResult = mockHost.getReferenceMarkers(fileName);
const refResult = mockHost.getReferenceMarkers(fileName) !;
let tests = 0;
for (const name in refResult.references) {
const references = refResult.references[name];
@ -144,12 +144,12 @@ describe('definitions', () => {
try {
cb(fileName, newContent);
} finally {
mockHost.override(fileName, undefined);
mockHost.override(fileName, undefined !);
}
}
});
function matchingSpan(aSpans: Span[], bSpans: Span[]): Span {
function matchingSpan(aSpans: Span[], bSpans: Span[]): Span|undefined {
for (const a of aSpans) {
for (const b of bSpans) {
if (a.start == b.start && a.end == b.end) {

View File

@ -32,9 +32,9 @@ describe('diagnostics', () => {
function diagnostics(template: string): Diagnostics {
try {
mockHost.override(fileName, template);
return ngService.getDiagnostics(fileName);
return ngService.getDiagnostics(fileName) !;
} finally {
mockHost.override(fileName, undefined);
mockHost.override(fileName, undefined !);
}
}
@ -84,10 +84,10 @@ describe('diagnostics', () => {
const code = '\n@Component({template: \'<div></div>\'}) export class MyComponent {}';
addCode(code, (fileName, content) => {
const diagnostics = ngService.getDiagnostics(fileName);
const offset = content.lastIndexOf('@Component') + 1;
const offset = content !.lastIndexOf('@Component') + 1;
const len = 'Component'.length;
includeDiagnostic(
diagnostics, 'Component \'MyComponent\' is not included in a module', offset, len);
diagnostics !, 'Component \'MyComponent\' is not included in a module', offset, len);
});
});
@ -95,7 +95,7 @@ describe('diagnostics', () => {
const code = '\n@Component({template: \'<form></form>\'}) export class MyComponent {}';
addCode(code, (fileName, content) => {
const diagnostics = ngService.getDiagnostics(fileName);
onlyModuleDiagnostics(diagnostics);
onlyModuleDiagnostics(diagnostics !);
});
});
@ -119,7 +119,7 @@ describe('diagnostics', () => {
addCode(code, (fileName, content) => {
const diagnostics = ngService.getDiagnostics(fileName);
includeDiagnostic(
diagnostics, 'Function calls are not supported.', '() => \'foo\'', content);
diagnostics !, 'Function calls are not supported.', '() => \'foo\'', content);
});
});
@ -134,7 +134,7 @@ describe('diagnostics', () => {
` @Component({template: \`<div *ngIf="something === 'foo'"></div>\`}) export class MyComponent { something: 'foo' | 'bar'; }`;
addCode(code, fileName => {
const diagnostics = ngService.getDiagnostics(fileName);
onlyModuleDiagnostics(diagnostics);
onlyModuleDiagnostics(diagnostics !);
});
});
@ -144,7 +144,7 @@ describe('diagnostics', () => {
addCode(code, (fileName, content) => {
const diagnostics = ngService.getDiagnostics(fileName);
includeDiagnostic(
diagnostics, 'Unexpected callable expression. Expected a method call', 'onClick',
diagnostics !, 'Unexpected callable expression. Expected a method call', 'onClick',
content);
});
});
@ -155,7 +155,7 @@ describe('diagnostics', () => {
` @Component({template: \`<div *ngIf="something === undefined"></div>\`}) export class MyComponent { something = 'foo'; }})`;
addCode(code, fileName => {
const diagnostics = ngService.getDiagnostics(fileName);
onlyModuleDiagnostics(diagnostics);
onlyModuleDiagnostics(diagnostics !);
});
});
@ -165,7 +165,7 @@ describe('diagnostics', () => {
` @Component({template: '<p> Using an invalid pipe {{data | dat}} </p>'}) export class MyComponent { data = 'some data'; }`;
addCode(code, fileName => {
const diagnostic =
ngService.getDiagnostics(fileName).filter(d => d.message.indexOf('pipe') > 0)[0];
ngService.getDiagnostics(fileName) !.filter(d => d.message.indexOf('pipe') > 0)[0];
expect(diagnostic).not.toBeUndefined();
expect(diagnostic.span.end - diagnostic.span.start).toBeLessThan(11);
});
@ -267,7 +267,7 @@ describe('diagnostics', () => {
try {
cb(fileName, newContent);
} finally {
mockHost.override(fileName, undefined);
mockHost.override(fileName, undefined !);
}
}

View File

@ -71,7 +71,7 @@ describe('hover', () => {
function hover(code: string, hoverText: string) {
addCode(code, fileName => {
let tests = 0;
const markers = mockHost.getReferenceMarkers(fileName);
const markers = mockHost.getReferenceMarkers(fileName) !;
const keys = Object.keys(markers.references).concat(Object.keys(markers.definitions));
for (const referenceName of keys) {
const references = (markers.references[referenceName] ||
@ -96,7 +96,7 @@ describe('hover', () => {
try {
cb(fileName, newContent);
} finally {
mockHost.override(fileName, undefined);
mockHost.override(fileName, undefined !);
}
}

View File

@ -22,7 +22,7 @@ describe('service without angular', () => {
let ngHost = new TypeScriptServiceHost(mockHost, service);
let ngService = createLanguageService(ngHost);
const fileName = '/app/test.ng';
let position = mockHost.getMarkerLocations(fileName)['h1-content'];
let position = mockHost.getMarkerLocations(fileName) !['h1-content'];
it('should not crash a get template references',
() => expect(() => ngService.getTemplateReferences()));

View File

@ -21,7 +21,7 @@ describe('references', () => {
let service: ts.LanguageService;
let program: ts.Program;
let ngHost: TypeScriptServiceHost;
let ngService: LanguageService = createLanguageService(ngHost);
let ngService: LanguageService = createLanguageService(undefined !);
beforeEach(() => {
mockHost = new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], toh);

View File

@ -63,7 +63,7 @@ missingCache.set(
missingCache.set('/node_modules/@angular/forms/src/directives/form_interface.metadata.json', true);
export class MockTypescriptHost implements ts.LanguageServiceHost {
private angularPath: string;
private angularPath: string|undefined;
private nodeModulesPath: string;
private scriptVersion = new Map<string, number>();
private overrides = new Map<string, string>();
@ -120,7 +120,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
return (this.scriptVersion.get(fileName) || 0).toString();
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined {
const content = this.getFileContent(fileName);
if (content) return ts.ScriptSnapshot.fromString(content);
return undefined;
@ -145,19 +145,19 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
}
}
getReferenceMarkers(fileName: string): ReferenceResult {
getReferenceMarkers(fileName: string): ReferenceResult|undefined {
let content = this.getRawFileContent(fileName);
if (content) {
return getReferenceMarkers(content);
}
}
getFileContent(fileName: string): string {
getFileContent(fileName: string): string|undefined {
const content = this.getRawFileContent(fileName);
if (content) return removeReferenceMarkers(removeLocationMarkers(content));
}
private getRawFileContent(fileName: string): string {
private getRawFileContent(fileName: string): string|undefined {
if (this.overrides.has(fileName)) {
return this.overrides.get(fileName);
}
@ -225,7 +225,7 @@ function find(fileName: string, data: MockData): MockData|undefined {
if (typeof current === 'string')
return undefined;
else
current = (<MockDirectory>current)[name];
current = (<MockDirectory>current)[name] !;
if (!current) return undefined;
}
return current;
@ -241,7 +241,7 @@ function open(fileName: string, data: MockData): string|undefined {
function directoryExists(dirname: string, data: MockData): boolean {
let result = find(dirname, data);
return result && typeof result !== 'string';
return !!result && typeof result !== 'string';
}
const locationMarker = /\~\{(\w+(-\w+)*)\}/g;

View File

@ -195,7 +195,7 @@ describe('plugin', () => {
});
function getMarkerLocation(fileName: string, locationMarker: string): number {
const location = mockHost.getMarkerLocations(fileName)[locationMarker];
const location = mockHost.getMarkerLocations(fileName) ![locationMarker];
if (location == null) {
throw new Error(`No marker ${locationMarker} found.`);
}

View File

@ -5,6 +5,7 @@
"stripInternal": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"module": "es2015",
"moduleResolution": "node",
"outDir": "../../dist/packages/language-service",

View File

@ -6,6 +6,7 @@
"emitDecoratorMetadata": true,
"module": "commonjs",
"moduleResolution": "node",
"strictNullChecks": false,
"outDir": "../dist/all/@angular",
"noImplicitAny": true,
"noFallthroughCasesInSwitch": true,