Merge remote-tracking branch 'origin/master' into tsserverVS-WIP

This commit is contained in:
Vladimir Matveev 2016-06-02 16:14:34 -07:00
commit 8e6f36258e
60 changed files with 3668 additions and 368 deletions

View file

@ -154,7 +154,8 @@ var harnessSources = harnessCoreSources.concat([
"tsconfigParsing.ts",
"commandLineParsing.ts",
"convertCompilerOptionsFromJson.ts",
"convertTypingOptionsFromJson.ts"
"convertTypingOptionsFromJson.ts",
"tsserverProjectSystem.ts"
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([

View file

@ -77,12 +77,14 @@ namespace ts {
// Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements...
IsBlockScopedContainer = 1 << 1,
HasLocals = 1 << 2,
// The current node is the container of a control flow path. The current control flow should
// be saved and restored, and a new control flow initialized within the container.
IsControlFlowContainer = 1 << 2,
// If the current node is a container that also container that also contains locals. Examples:
//
// Functions, Methods, Modules, Source-files.
IsContainerWithLocals = IsContainer | HasLocals
IsFunctionLike = 1 << 3,
IsFunctionExpression = 1 << 4,
HasLocals = 1 << 5,
IsInterface = 1 << 6,
}
const binder = createBinder();
@ -103,22 +105,19 @@ namespace ts {
let lastContainer: Node;
let seenThisKeyword: boolean;
// state used by reachability checks
let hasExplicitReturn: boolean;
// state used by control flow analysis
let currentFlow: FlowNode;
let currentBreakTarget: FlowLabel;
let currentContinueTarget: FlowLabel;
let currentReturnTarget: FlowLabel;
let currentTrueTarget: FlowLabel;
let currentFalseTarget: FlowLabel;
let preSwitchCaseFlow: FlowNode;
let activeLabels: ActiveLabel[];
let hasExplicitReturn: boolean;
// state used for emit helpers
let hasClassExtends: boolean;
let hasAsyncFunctions: boolean;
let hasDecorators: boolean;
let hasParameterDecorators: boolean;
let hasJsxSpreadAttribute: boolean;
let emitFlags: NodeFlags;
// If this file is an external module, then it is automatically in strict-mode according to
// ES6. If it is not an external module, then we'll determine if it is in strict mode or
@ -156,18 +155,15 @@ namespace ts {
blockScopeContainer = undefined;
lastContainer = undefined;
seenThisKeyword = false;
hasExplicitReturn = false;
currentFlow = undefined;
currentBreakTarget = undefined;
currentContinueTarget = undefined;
currentReturnTarget = undefined;
currentTrueTarget = undefined;
currentFalseTarget = undefined;
activeLabels = undefined;
hasClassExtends = false;
hasAsyncFunctions = false;
hasDecorators = false;
hasParameterDecorators = false;
hasJsxSpreadAttribute = false;
hasExplicitReturn = false;
emitFlags = NodeFlags.None;
}
return bindSourceFile;
@ -267,6 +263,18 @@ namespace ts {
let functionType = <JSDocFunctionType>node.parent;
let index = indexOf(functionType.parameters, node);
return "p" + index;
case SyntaxKind.JSDocTypedefTag:
const parentNode = node.parent && node.parent.parent;
let nameFromParentNode: string;
if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) {
if ((<VariableStatement>parentNode).declarationList.declarations.length > 0) {
const nameIdentifier = (<VariableStatement>parentNode).declarationList.declarations[0].name;
if (nameIdentifier.kind === SyntaxKind.Identifier) {
nameFromParentNode = (<Identifier>nameIdentifier).text;
}
}
}
return nameFromParentNode;
}
}
@ -400,17 +408,13 @@ namespace ts {
// All container nodes are kept on a linked list in declaration order. This list is used by
// the getLocalNameOfContainer function in the type checker to validate that the local name
// used for a container is unique.
function bindChildren(node: Node) {
function bindContainer(node: Node, containerFlags: ContainerFlags) {
// Before we recurse into a node's children, we first save the existing parent, container
// and block-container. Then after we pop out of processing the children, we restore
// these saved values.
const saveParent = parent;
const saveContainer = container;
const savedBlockScopeContainer = blockScopeContainer;
// This node will now be set as the parent of all of its children as we recurse into them.
parent = node;
// Depending on what kind of node this is, we may have to adjust the current container
// and block-container. If the current node is a container, then it is automatically
// considered the current block-container as well. Also, for containers that we know
@ -428,115 +432,90 @@ namespace ts {
// reusing a node from a previous compilation, that node may have had 'locals' created
// for it. We must clear this so we don't accidentally move any stale data forward from
// a previous compilation.
const containerFlags = getContainerFlags(node);
if (containerFlags & ContainerFlags.IsContainer) {
container = blockScopeContainer = node;
if (containerFlags & ContainerFlags.HasLocals) {
container.locals = {};
}
addToContainerChain(container);
}
else if (containerFlags & ContainerFlags.IsBlockScopedContainer) {
blockScopeContainer = node;
blockScopeContainer.locals = undefined;
}
let savedHasExplicitReturn: boolean;
let savedCurrentFlow: FlowNode;
let savedBreakTarget: FlowLabel;
let savedContinueTarget: FlowLabel;
let savedActiveLabels: ActiveLabel[];
const kind = node.kind;
let flags = node.flags;
// reset all reachability check related flags on node (for incremental scenarios)
flags &= ~NodeFlags.ReachabilityCheckFlags;
// reset all emit helper flags on node (for incremental scenarios)
flags &= ~NodeFlags.EmitHelperFlags;
if (kind === SyntaxKind.InterfaceDeclaration) {
seenThisKeyword = false;
}
const saveState = kind === SyntaxKind.SourceFile || kind === SyntaxKind.ModuleBlock || isFunctionLikeKind(kind);
if (saveState) {
savedHasExplicitReturn = hasExplicitReturn;
savedCurrentFlow = currentFlow;
savedBreakTarget = currentBreakTarget;
savedContinueTarget = currentContinueTarget;
savedActiveLabels = activeLabels;
hasExplicitReturn = false;
currentFlow = { flags: FlowFlags.Start };
if (containerFlags & ContainerFlags.IsControlFlowContainer) {
const saveCurrentFlow = currentFlow;
const saveBreakTarget = currentBreakTarget;
const saveContinueTarget = currentContinueTarget;
const saveReturnTarget = currentReturnTarget;
const saveActiveLabels = activeLabels;
const saveHasExplicitReturn = hasExplicitReturn;
const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !!getImmediatelyInvokedFunctionExpression(node);
// An IIFE is considered part of the containing control flow. Return statements behave
// similarly to break statements that exit to a label just past the statement body.
if (isIIFE) {
currentReturnTarget = createBranchLabel();
}
else {
currentFlow = { flags: FlowFlags.Start };
if (containerFlags & ContainerFlags.IsFunctionExpression) {
(<FlowStart>currentFlow).container = <FunctionExpression | ArrowFunction>node;
}
currentReturnTarget = undefined;
}
currentBreakTarget = undefined;
currentContinueTarget = undefined;
activeLabels = undefined;
}
if (isInJavaScriptFile(node) && node.jsDocComment) {
bind(node.jsDocComment);
}
bindReachableStatement(node);
if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
flags |= NodeFlags.HasImplicitReturn;
if (hasExplicitReturn) {
flags |= NodeFlags.HasExplicitReturn;
hasExplicitReturn = false;
bindChildren(node);
// Reset all reachability check related flags on node (for incremental scenarios)
// Reset all emit helper flags on node (for incremental scenarios)
node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
node.flags |= NodeFlags.HasImplicitReturn;
if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
}
if (node.kind === SyntaxKind.SourceFile) {
node.flags |= emitFlags;
}
if (isIIFE) {
addAntecedent(currentReturnTarget, currentFlow);
currentFlow = finishFlowLabel(currentReturnTarget);
}
else {
currentFlow = saveCurrentFlow;
}
currentBreakTarget = saveBreakTarget;
currentContinueTarget = saveContinueTarget;
currentReturnTarget = saveReturnTarget;
activeLabels = saveActiveLabels;
hasExplicitReturn = saveHasExplicitReturn;
}
if (kind === SyntaxKind.InterfaceDeclaration) {
flags = seenThisKeyword ? flags | NodeFlags.ContainsThis : flags & ~NodeFlags.ContainsThis;
else if (containerFlags & ContainerFlags.IsInterface) {
seenThisKeyword = false;
bindChildren(node);
node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis;
}
if (kind === SyntaxKind.SourceFile) {
if (hasClassExtends) {
flags |= NodeFlags.HasClassExtends;
}
if (hasDecorators) {
flags |= NodeFlags.HasDecorators;
}
if (hasParameterDecorators) {
flags |= NodeFlags.HasParamDecorators;
}
if (hasAsyncFunctions) {
flags |= NodeFlags.HasAsyncFunctions;
}
if (hasJsxSpreadAttribute) {
flags |= NodeFlags.HasJsxSpreadAttribute;
}
else {
bindChildren(node);
}
node.flags = flags;
if (saveState) {
hasExplicitReturn = savedHasExplicitReturn;
currentFlow = savedCurrentFlow;
currentBreakTarget = savedBreakTarget;
currentContinueTarget = savedContinueTarget;
activeLabels = savedActiveLabels;
}
container = saveContainer;
parent = saveParent;
blockScopeContainer = savedBlockScopeContainer;
}
/**
* Returns true if node and its subnodes were successfully traversed.
* Returning false means that node was not examined and caller needs to dive into the node himself.
*/
function bindReachableStatement(node: Node): void {
function bindChildren(node: Node): void {
// Binding of JsDocComment should be done before the current block scope container changes.
// because the scope of JsDocComment should not be affected by whether the current node is a
// container or not.
if (isInJavaScriptFile(node) && node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
bind(jsDocComment);
}
}
if (checkUnreachable(node)) {
forEachChild(node, bind);
return;
}
switch (node.kind) {
case SyntaxKind.WhileStatement:
bindWhileStatement(<WhileStatement>node);
@ -589,6 +568,9 @@ namespace ts {
case SyntaxKind.VariableDeclaration:
bindVariableDeclarationFlow(<VariableDeclaration>node);
break;
case SyntaxKind.CallExpression:
bindCallExpressionFlow(<CallExpression>node);
break;
default:
forEachChild(node, bind);
break;
@ -848,6 +830,9 @@ namespace ts {
bind(node.expression);
if (node.kind === SyntaxKind.ReturnStatement) {
hasExplicitReturn = true;
if (currentReturnTarget) {
addAntecedent(currentReturnTarget, currentFlow);
}
}
currentFlow = unreachableFlow;
}
@ -1098,35 +1083,67 @@ namespace ts {
}
}
function bindCallExpressionFlow(node: CallExpression) {
// If the target of the call expression is a function expression or arrow function we have
// an immediately invoked function expression (IIFE). Initialize the flowNode property to
// the current control flow (which includes evaluation of the IIFE arguments).
let expr: Expression = node.expression;
while (expr.kind === SyntaxKind.ParenthesizedExpression) {
expr = (<ParenthesizedExpression>expr).expression;
}
if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) {
forEach(node.typeArguments, bind);
forEach(node.arguments, bind);
bind(node.expression);
}
else {
forEachChild(node, bind);
}
}
function getContainerFlags(node: Node): ContainerFlags {
switch (node.kind) {
case SyntaxKind.ClassExpression:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocRecordType:
return ContainerFlags.IsContainer;
case SyntaxKind.InterfaceDeclaration:
return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.TypeAliasDeclaration:
return ContainerFlags.IsContainer | ContainerFlags.HasLocals;
case SyntaxKind.SourceFile:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals;
case SyntaxKind.Constructor:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionType:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ConstructorType:
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.SourceFile:
case SyntaxKind.TypeAliasDeclaration:
return ContainerFlags.IsContainerWithLocals;
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression;
case SyntaxKind.ModuleBlock:
return ContainerFlags.IsControlFlowContainer;
case SyntaxKind.PropertyDeclaration:
return (<PropertyDeclaration>node).initializer ? ContainerFlags.IsControlFlowContainer : 0;
case SyntaxKind.CatchClause:
case SyntaxKind.ForStatement:
@ -1194,6 +1211,7 @@ namespace ts {
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.JSDocRecordType:
case SyntaxKind.JSDocTypeLiteral:
// Interface/Object-types always have their children added to the 'members' of
// their container. They are only accessible through an instance of their
// container, and are never in scope otherwise (even inside the body of the
@ -1222,7 +1240,7 @@ namespace ts {
// their container in the tree. To accomplish this, we simply add their declared
// symbol to the 'locals' of the container. These symbols can then be found as
// the type checker walks up the containers, checking them for matching names.
return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes);
return declareSymbol(container.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes);
}
}
@ -1560,15 +1578,9 @@ namespace ts {
if (!node) {
return;
}
node.parent = parent;
const savedInStrictMode = inStrictMode;
if (!savedInStrictMode) {
updateStrictMode(node);
}
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
const saveInStrictMode = inStrictMode;
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
// and then potentially add the symbol to an appropriate symbol table. Possible
// destination symbol tables are:
//
@ -1576,47 +1588,40 @@ namespace ts {
// 2) The 'members' table of the current container's symbol.
// 3) The 'locals' table of the current container.
//
// However, not all symbols will end up in any of these tables. 'Anonymous' symbols
// However, not all symbols will end up in any of these tables. 'Anonymous' symbols
// (like TypeLiterals for example) will not be put in any table.
bindWorker(node);
// Then we recurse into the children of the node to bind them as well. For certain
// symbols we do specialized work when we recurse. For example, we'll keep track of
// the current 'container' node when it changes. This helps us know which symbol table
// a local should go into for example.
bindChildren(node);
inStrictMode = savedInStrictMode;
}
function updateStrictMode(node: Node) {
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.ModuleBlock:
updateStrictModeStatementList((<SourceFile | ModuleBlock>node).statements);
return;
case SyntaxKind.Block:
if (isFunctionLike(node.parent)) {
updateStrictModeStatementList((<Block>node).statements);
}
return;
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
// All classes are automatically in strict mode in ES6.
inStrictMode = true;
return;
// Then we recurse into the children of the node to bind them as well. For certain
// symbols we do specialized work when we recurse. For example, we'll keep track of
// the current 'container' node when it changes. This helps us know which symbol table
// a local should go into for example. Since terminal nodes are known not to have
// children, as an optimization we don't process those.
if (node.kind > SyntaxKind.LastToken) {
const saveParent = parent;
parent = node;
const containerFlags = getContainerFlags(node);
if (containerFlags === ContainerFlags.None) {
bindChildren(node);
}
else {
bindContainer(node, containerFlags);
}
parent = saveParent;
}
inStrictMode = saveInStrictMode;
}
function updateStrictModeStatementList(statements: NodeArray<Statement>) {
for (const statement of statements) {
if (!isPrologueDirective(statement)) {
return;
}
if (!inStrictMode) {
for (const statement of statements) {
if (!isPrologueDirective(statement)) {
return;
}
if (isUseStrictPrologueDirective(<ExpressionStatement>statement)) {
inStrictMode = true;
return;
if (isUseStrictPrologueDirective(<ExpressionStatement>statement)) {
inStrictMode = true;
return;
}
}
}
}
@ -1696,6 +1701,8 @@ namespace ts {
case SyntaxKind.PropertySignature:
case SyntaxKind.JSDocRecordMember:
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property | ((<PropertyDeclaration>node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
case SyntaxKind.JSDocPropertyTag:
return bindJSDocProperty(<JSDocPropertyTag>node);
case SyntaxKind.PropertyAssignment:
case SyntaxKind.ShorthandPropertyAssignment:
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
@ -1703,7 +1710,7 @@ namespace ts {
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes);
case SyntaxKind.JsxSpreadAttribute:
hasJsxSpreadAttribute = true;
emitFlags |= NodeFlags.HasJsxSpreadAttribute;
return;
case SyntaxKind.CallSignature:
@ -1731,6 +1738,7 @@ namespace ts {
case SyntaxKind.JSDocFunctionType:
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocRecordType:
return bindAnonymousDeclaration(<TypeLiteralNode>node, SymbolFlags.TypeLiteral, "__type");
case SyntaxKind.ObjectLiteralExpression:
@ -1748,9 +1756,12 @@ namespace ts {
// Members of classes, interfaces, and modules
case SyntaxKind.ClassExpression:
case SyntaxKind.ClassDeclaration:
// All classes are automatically in strict mode in ES6.
inStrictMode = true;
return bindClassLikeDeclaration(<ClassLikeDeclaration>node);
case SyntaxKind.InterfaceDeclaration:
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes);
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.TypeAliasDeclaration:
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
case SyntaxKind.EnumDeclaration:
@ -1773,7 +1784,15 @@ namespace ts {
case SyntaxKind.ExportAssignment:
return bindExportAssignment(<ExportAssignment>node);
case SyntaxKind.SourceFile:
updateStrictModeStatementList((<SourceFile>node).statements);
return bindSourceFileIfExternalModule();
case SyntaxKind.Block:
if (!isFunctionLike(node.parent)) {
return;
}
// Fall through
case SyntaxKind.ModuleBlock:
return updateStrictModeStatementList((<Block | ModuleBlock>node).statements);
}
}
@ -1935,10 +1954,10 @@ namespace ts {
function bindClassLikeDeclaration(node: ClassLikeDeclaration) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (getClassExtendsHeritageClauseElement(node) !== undefined) {
hasClassExtends = true;
emitFlags |= NodeFlags.HasClassExtends;
}
if (nodeIsDecorated(node)) {
hasDecorators = true;
emitFlags |= NodeFlags.HasDecorators;
}
}
@ -2014,8 +2033,7 @@ namespace ts {
if (!isDeclarationFile(file) &&
!isInAmbientContext(node) &&
nodeIsDecorated(node)) {
hasDecorators = true;
hasParameterDecorators = true;
emitFlags |= (NodeFlags.HasDecorators | NodeFlags.HasParamDecorators);
}
if (inStrictMode) {
@ -2042,7 +2060,7 @@ namespace ts {
function bindFunctionDeclaration(node: FunctionDeclaration) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
emitFlags |= NodeFlags.HasAsyncFunctions;
}
}
@ -2059,10 +2077,12 @@ namespace ts {
function bindFunctionExpression(node: FunctionExpression) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
emitFlags |= NodeFlags.HasAsyncFunctions;
}
}
if (currentFlow) {
node.flowNode = currentFlow;
}
checkStrictModeFunctionName(<FunctionExpression>node);
const bindingName = (<FunctionExpression>node).name ? (<FunctionExpression>node).name.text : "__function";
return bindAnonymousDeclaration(<FunctionExpression>node, SymbolFlags.Function, bindingName);
@ -2071,10 +2091,10 @@ namespace ts {
function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
if (isAsyncFunctionLike(node)) {
hasAsyncFunctions = true;
emitFlags |= NodeFlags.HasAsyncFunctions;
}
if (nodeIsDecorated(node)) {
hasDecorators = true;
emitFlags |= NodeFlags.HasDecorators;
}
}
@ -2083,6 +2103,10 @@ namespace ts {
: declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes);
}
function bindJSDocProperty(node: JSDocPropertyTag) {
return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes);
}
// reachability checks
function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean {

View file

@ -110,16 +110,16 @@ namespace ts {
const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
const nullableWideningFlags = strictNullChecks ? 0 : TypeFlags.ContainsUndefinedOrNull;
const anyType = createIntrinsicType(TypeFlags.Any, "any");
const stringType = createIntrinsicType(TypeFlags.String, "string");
const numberType = createIntrinsicType(TypeFlags.Number, "number");
const booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
const voidType = createIntrinsicType(TypeFlags.Void, "void");
const undefinedType = createIntrinsicType(TypeFlags.Undefined | nullableWideningFlags, "undefined");
const nullType = createIntrinsicType(TypeFlags.Null | nullableWideningFlags, "null");
const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined");
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
const nullType = createIntrinsicType(TypeFlags.Null, "null");
const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
const neverType = createIntrinsicType(TypeFlags.Never, "never");
@ -3405,7 +3405,7 @@ namespace ts {
error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol));
return type.resolvedBaseConstructorType = unknownType;
}
if (baseConstructorType !== unknownType && baseConstructorType !== nullType && !isConstructorType(baseConstructorType)) {
if (baseConstructorType !== unknownType && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) {
error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType));
return type.resolvedBaseConstructorType = unknownType;
}
@ -3581,8 +3581,22 @@ namespace ts {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) {
return unknownType;
}
const declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
let type = getTypeFromTypeNode(declaration.type);
let type: Type;
let declaration: JSDocTypedefTag | TypeAliasDeclaration = <JSDocTypedefTag>getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag);
if (declaration) {
if (declaration.jsDocTypeLiteral) {
type = getTypeFromTypeNode(declaration.jsDocTypeLiteral);
}
else {
type = getTypeFromTypeNode(declaration.typeExpression.type);
}
}
else {
declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
type = getTypeFromTypeNode(declaration.type);
}
if (popTypeResolution()) {
links.typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
if (links.typeParameters) {
@ -4997,6 +5011,7 @@ namespace ts {
containsAny?: boolean;
containsUndefined?: boolean;
containsNull?: boolean;
containsNonWideningType?: boolean;
}
function addTypeToSet(typeSet: TypeSet, type: Type, typeSetKind: TypeFlags) {
@ -5007,6 +5022,7 @@ namespace ts {
if (type.flags & TypeFlags.Any) typeSet.containsAny = true;
if (type.flags & TypeFlags.Undefined) typeSet.containsUndefined = true;
if (type.flags & TypeFlags.Null) typeSet.containsNull = true;
if (!(type.flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true;
}
else if (type !== neverType && !contains(typeSet, type)) {
typeSet.push(type);
@ -5067,8 +5083,8 @@ namespace ts {
removeSubtypes(typeSet);
}
if (typeSet.length === 0) {
return typeSet.containsNull ? nullType :
typeSet.containsUndefined ? undefinedType :
return typeSet.containsNull ? typeSet.containsNonWideningType ? nullType : nullWideningType :
typeSet.containsUndefined ? typeSet.containsNonWideningType ? undefinedType : undefinedWideningType :
neverType;
}
else if (typeSet.length === 1) {
@ -5253,6 +5269,7 @@ namespace ts {
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.JSDocRecordType:
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
@ -5867,7 +5884,7 @@ namespace ts {
if (!(target.flags & TypeFlags.Never)) {
if (target.flags & TypeFlags.Any || source.flags & TypeFlags.Never) return Ternary.True;
if (source.flags & TypeFlags.Undefined) {
if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void) || source === emptyArrayElementType) return Ternary.True;
if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void)) return Ternary.True;
}
if (source.flags & TypeFlags.Null) {
if (!strictNullChecks || target.flags & TypeFlags.Null) return Ternary.True;
@ -6957,7 +6974,7 @@ namespace ts {
if (type.flags & TypeFlags.ObjectLiteral) {
for (const p of getPropertiesOfObjectType(type)) {
const t = getTypeOfSymbol(p);
if (t.flags & TypeFlags.ContainsUndefinedOrNull) {
if (t.flags & TypeFlags.ContainsWideningType) {
if (!reportWideningErrorsInType(t)) {
error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t)));
}
@ -7004,7 +7021,7 @@ namespace ts {
}
function reportErrorsFromWidening(declaration: Declaration, type: Type) {
if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefinedOrNull) {
if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsWideningType) {
// Report implicit any error within type if possible, otherwise report error on declaration
if (!reportWideningErrorsInType(type)) {
reportImplicitAnyError(declaration, type);
@ -7645,7 +7662,7 @@ namespace ts {
getInitialTypeOfBindingElement(<BindingElement>node);
}
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean) {
function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
let key: string;
if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
return declaredType;
@ -7691,15 +7708,21 @@ namespace ts {
getTypeAtFlowBranchLabel(<FlowLabel>flow) :
getTypeAtFlowLoopLabel(<FlowLabel>flow);
}
else if (flow.flags & FlowFlags.Unreachable) {
else if (flow.flags & FlowFlags.Start) {
// Check if we should continue with the control flow of the containing function.
const container = (<FlowStart>flow).container;
if (container && includeOuterFunctions) {
flow = container.flowNode;
continue;
}
// At the top of the flow we have the initial type.
type = initialType;
}
else {
// Unreachable code errors are reported in the binding phase. Here we
// simply return the declared type to reduce follow-on errors.
type = declaredType;
}
else {
// At the top of the flow we have the initial type.
type = initialType;
}
if (flow.flags & FlowFlags.Shared) {
// Record visited node and the associated type in the cache.
visitedFlowNodes[visitedFlowCount] = flow;
@ -8068,6 +8091,17 @@ namespace ts {
return expression;
}
function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) {
const declarationContainer = getContainingFunctionOrModule(declaration);
let container = getContainingFunctionOrModule(reference);
while (container !== declarationContainer &&
(container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) &&
(includeOuterFunctions || getImmediatelyInvokedFunctionExpression(<FunctionExpression>container))) {
container = getContainingFunctionOrModule(container);
}
return container === declarationContainer;
}
function checkIdentifier(node: Identifier): Type {
const symbol = getResolvedSymbol(node);
@ -8124,10 +8158,11 @@ namespace ts {
return type;
}
const declaration = localOrExportSymbol.valueDeclaration;
const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol);
const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration ||
getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) ||
getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized);
!isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions);
const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions);
if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) {
error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
// Return the declared type to reduce follow-on errors
@ -8278,7 +8313,7 @@ namespace ts {
const classInstanceType = <InterfaceType>getDeclaredTypeOfSymbol(classSymbol);
const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType);
return baseConstructorType === nullType;
return baseConstructorType === nullWideningType;
}
function checkThisExpression(node: Node): Type {
@ -8376,7 +8411,7 @@ namespace ts {
if (isClassLike(container.parent)) {
const symbol = getSymbolOfNode(container.parent);
const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType;
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true);
return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*includeOuterFunctions*/ true);
}
if (isInJavaScriptFile(node)) {
@ -8661,20 +8696,6 @@ namespace ts {
return undefined;
}
function getImmediatelyInvokedFunctionExpression(func: FunctionExpression | MethodDeclaration) {
if (isFunctionExpressionOrArrowFunction(func)) {
let prev: Node = func;
let parent: Node = func.parent;
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
prev = parent;
parent = parent.parent;
}
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
return parent as CallExpression;
}
}
}
// In a variable, parameter or property declaration with a type annotation,
// the contextual type of an initializer expression is the type of the variable, parameter or property.
// Otherwise, in a parameter declaration of a contextually typed function expression,
@ -9183,7 +9204,7 @@ namespace ts {
}
}
}
return createArrayType(elementTypes.length ? getUnionType(elementTypes) : emptyArrayElementType);
return createArrayType(elementTypes.length ? getUnionType(elementTypes) : strictNullChecks ? neverType : undefinedWideningType);
}
function isNumericName(name: DeclarationName): boolean {
@ -9988,7 +10009,7 @@ namespace ts {
return propType;
}
}
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true);
return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false);
}
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
@ -11983,7 +12004,7 @@ namespace ts {
function checkVoidExpression(node: VoidExpression): Type {
checkExpression(node.expression);
return undefinedType;
return undefinedWideningType;
}
function checkAwaitExpression(node: AwaitExpression): Type {
@ -12390,7 +12411,7 @@ namespace ts {
case SyntaxKind.InKeyword:
return checkInExpression(left, right, leftType, rightType);
case SyntaxKind.AmpersandAmpersandToken:
return addNullableKind(rightType, getNullableKind(leftType));
return strictNullChecks ? addNullableKind(rightType, getNullableKind(leftType)) : rightType;
case SyntaxKind.BarBarToken:
return getUnionType([getNonNullableType(leftType), rightType]);
case SyntaxKind.EqualsToken:
@ -12657,7 +12678,7 @@ namespace ts {
case SyntaxKind.SuperKeyword:
return checkSuperExpression(node);
case SyntaxKind.NullKeyword:
return nullType;
return nullWideningType;
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return booleanType;
@ -12715,7 +12736,7 @@ namespace ts {
case SyntaxKind.SpreadElementExpression:
return checkSpreadElementExpression(<SpreadElementExpression>node, contextualMapper);
case SyntaxKind.OmittedExpression:
return undefinedType;
return undefinedWideningType;
case SyntaxKind.YieldExpression:
return checkYieldExpression(<YieldExpression>node);
case SyntaxKind.JsxExpression:
@ -16724,7 +16745,7 @@ namespace ts {
node = node.parent;
}
return node.parent && node.parent.kind === SyntaxKind.TypeReference;
return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference) ;
}
function isHeritageClauseElementIdentifier(entityName: Node): boolean {
@ -16860,7 +16881,7 @@ namespace ts {
}
}
else if (isTypeReferenceIdentifier(<EntityName>entityName)) {
let meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace;
let meaning = (entityName.parent.kind === SyntaxKind.TypeReference || entityName.parent.kind === SyntaxKind.JSDocTypeReference) ? SymbolFlags.Type : SymbolFlags.Namespace;
// Include aliases in the meaning, this ensures that we do not follow aliases to where they point and instead
// return the alias symbol.
meaning |= SymbolFlags.Alias;
@ -17629,7 +17650,7 @@ namespace ts {
// Setup global builtins
addToSymbolTable(globals, builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0);
getSymbolLinks(undefinedSymbol).type = undefinedType;
getSymbolLinks(undefinedSymbol).type = undefinedWideningType;
getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments");
getSymbolLinks(unknownSymbol).type = unknownType;

View file

@ -819,6 +819,10 @@
"category": "Error",
"code": 1252
},
"'{0}' tag cannot be used independently as a top level JSDoc tag.": {
"category": "Error",
"code": 1253
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
"code": 1300

View file

@ -401,6 +401,15 @@ namespace ts {
return visitNode(cbNode, (<JSDocTypeTag>node).typeExpression);
case SyntaxKind.JSDocTemplateTag:
return visitNodes(cbNodes, (<JSDocTemplateTag>node).typeParameters);
case SyntaxKind.JSDocTypedefTag:
return visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocTypedefTag>node).name) ||
visitNode(cbNode, (<JSDocTypedefTag>node).jsDocTypeLiteral);
case SyntaxKind.JSDocTypeLiteral:
return visitNodes(cbNodes, (<JSDocTypeLiteral>node).jsDocPropertyTags);
case SyntaxKind.JSDocPropertyTag:
return visitNode(cbNode, (<JSDocPropertyTag>node).typeExpression) ||
visitNode(cbNode, (<JSDocPropertyTag>node).name);
}
}
@ -431,7 +440,14 @@ namespace ts {
/* @internal */
export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) {
return Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
if (result && result.jsDocComment) {
// because the jsDocComment was parsed out of the source file, it might
// not be covered by the fixupParentReferences.
Parser.fixupParentReferences(result.jsDocComment);
}
return result;
}
/* @internal */
@ -628,9 +644,14 @@ namespace ts {
if (comments) {
for (const comment of comments) {
const jsDocComment = JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos);
if (jsDocComment) {
node.jsDocComment = jsDocComment;
if (!jsDocComment) {
continue;
}
if (!node.jsDocComments) {
node.jsDocComments = [];
}
node.jsDocComments.push(jsDocComment);
}
}
}
@ -638,14 +659,14 @@ namespace ts {
return node;
}
export function fixupParentReferences(sourceFile: Node) {
export function fixupParentReferences(rootNode: Node) {
// normally parent references are set during binding. However, for clients that only need
// a syntax tree, and no semantic features, then the binding process is an unnecessary
// overhead. This functions allows us to set all the parents, without all the expense of
// binding.
let parent: Node = sourceFile;
forEachChild(sourceFile, visitNode);
let parent: Node = rootNode;
forEachChild(rootNode, visitNode);
return;
function visitNode(n: Node): void {
@ -658,6 +679,13 @@ namespace ts {
const saveParent = parent;
parent = n;
forEachChild(n, visitNode);
if (n.jsDocComments) {
for (const jsDocComment of n.jsDocComments) {
jsDocComment.parent = n;
parent = jsDocComment;
forEachChild(jsDocComment, visitNode);
}
}
parent = saveParent;
}
}
@ -2704,7 +2732,7 @@ namespace ts {
// 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]
// 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]
// Production (1) of AsyncArrowFunctionExpression is parsed in "tryParseAsyncSimpleArrowFunctionExpression".
// And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression".
// And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression".
//
// If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is
// not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done
@ -3396,8 +3424,8 @@ namespace ts {
if (sourceFile.languageVariant !== LanguageVariant.JSX) {
return false;
}
// We are in JSX context and the token is part of JSXElement.
// Fall through
// We are in JSX context and the token is part of JSXElement.
// Fall through
default:
return true;
}
@ -4099,9 +4127,9 @@ namespace ts {
const isAsync = !!(node.flags & NodeFlags.Async);
node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
isAsync ? doInAwaitContext(parseOptionalIdentifier) :
parseOptionalIdentifier();
fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node);
node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false);
@ -5891,7 +5919,7 @@ namespace ts {
}
function checkForEmptyTypeArgumentList(typeArguments: NodeArray<Node>) {
if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) {
if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) {
const start = typeArguments.pos - "<".length;
const end = skipTrivia(sourceText, typeArguments.end) + ">".length;
return parseErrorAtPosition(start, end - start, Diagnostics.Type_argument_list_cannot_be_empty);
@ -6052,7 +6080,6 @@ namespace ts {
Debug.assert(end <= content.length);
let tags: NodeArray<JSDocTag>;
let result: JSDocComment;
// Check for /** (JSDoc opening part)
@ -6159,6 +6186,8 @@ namespace ts {
return handleTemplateTag(atToken, tagName);
case "type":
return handleTypeTag(atToken, tagName);
case "typedef":
return handleTypedefTag(atToken, tagName);
}
}
@ -6266,6 +6295,122 @@ namespace ts {
return finishNode(result);
}
function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
const name = parseJSDocIdentifierName();
if (!name) {
parseErrorAtPosition(scanner.getStartPos(), /*length*/ 0, Diagnostics.Identifier_expected);
return undefined;
}
const result = <JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
result.name = name;
result.typeExpression = typeExpression;
return finishNode(result);
}
function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
const typedefTag = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
typedefTag.atToken = atToken;
typedefTag.tagName = tagName;
typedefTag.name = parseJSDocIdentifierName();
typedefTag.typeExpression = typeExpression;
if (typeExpression) {
if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
const name = <Identifier>jsDocTypeReference.name;
if (name.text === "Object") {
typedefTag.jsDocTypeLiteral = scanChildTags();
}
}
}
if (!typedefTag.jsDocTypeLiteral) {
typedefTag.jsDocTypeLiteral = typeExpression.type;
}
}
else {
typedefTag.jsDocTypeLiteral = scanChildTags();
}
return finishNode(typedefTag);
function scanChildTags(): JSDocTypeLiteral {
const jsDocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, scanner.getStartPos());
let resumePos = scanner.getStartPos();
let canParseTag = true;
let seenAsterisk = false;
let parentTagTerminated = false;
while (token !== SyntaxKind.EndOfFileToken && !parentTagTerminated) {
nextJSDocToken();
switch (token) {
case SyntaxKind.AtToken:
if (canParseTag) {
parentTagTerminated = !tryParseChildTag(jsDocTypeLiteral);
}
seenAsterisk = false;
break;
case SyntaxKind.NewLineTrivia:
resumePos = scanner.getStartPos() - 1;
canParseTag = true;
seenAsterisk = false;
break;
case SyntaxKind.AsteriskToken:
if (seenAsterisk) {
canParseTag = false;
}
seenAsterisk = true;
break;
case SyntaxKind.Identifier:
canParseTag = false;
case SyntaxKind.EndOfFileToken:
break;
}
}
scanner.setTextPos(resumePos);
return finishNode(jsDocTypeLiteral);
}
}
function tryParseChildTag(parentTag: JSDocTypeLiteral): boolean {
Debug.assert(token === SyntaxKind.AtToken);
const atToken = createNode(SyntaxKind.AtToken, scanner.getStartPos());
atToken.end = scanner.getTextPos();
nextJSDocToken();
const tagName = parseJSDocIdentifierName();
if (!tagName) {
return false;
}
switch (tagName.text) {
case "type":
if (parentTag.jsDocTypeTag) {
// already has a @type tag, terminate the parent tag now.
return false;
}
parentTag.jsDocTypeTag = handleTypeTag(atToken, tagName);
return true;
case "prop":
case "property":
if (!parentTag.jsDocPropertyTags) {
parentTag.jsDocPropertyTags = <NodeArray<JSDocPropertyTag>>[];
}
const propertyTag = handlePropertyTag(atToken, tagName);
parentTag.jsDocPropertyTags.push(propertyTag);
return true;
}
return false;
}
function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag {
if (forEach(tags, t => t.kind === SyntaxKind.JSDocTemplateTag)) {
parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text);
@ -6435,10 +6580,6 @@ namespace ts {
node._children = undefined;
}
if (node.jsDocComment) {
node.jsDocComment = undefined;
}
node.pos += delta;
node.end += delta;
@ -6447,6 +6588,11 @@ namespace ts {
}
forEachChild(node, visitNode, visitArray);
if (node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
forEachChild(jsDocComment, visitNode, visitArray);
}
}
checkNodePositions(node, aggressiveChecks);
}

View file

@ -434,7 +434,7 @@ namespace ts {
}
/* @internal */
export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean): number {
export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean, stopAtComments = false): number {
// Using ! with a greater than test is a fast way of testing the following conditions:
// pos === undefined || pos === null || isNaN(pos) || pos < 0;
if (!(pos >= 0)) {
@ -462,6 +462,9 @@ namespace ts {
pos++;
continue;
case CharacterCodes.slash:
if (stopAtComments) {
break;
}
if (text.charCodeAt(pos + 1) === CharacterCodes.slash) {
pos += 2;
while (pos < text.length) {

View file

@ -343,6 +343,9 @@ namespace ts {
JSDocReturnTag,
JSDocTypeTag,
JSDocTemplateTag,
JSDocTypedefTag,
JSDocPropertyTag,
JSDocTypeLiteral,
// Synthesized list
SyntaxList,
@ -372,6 +375,10 @@ namespace ts {
FirstBinaryOperator = LessThanToken,
LastBinaryOperator = CaretEqualsToken,
FirstNode = QualifiedName,
FirstJSDocNode = JSDocTypeExpression,
LastJSDocNode = JSDocTypeLiteral,
FirstJSDocTagNode = JSDocComment,
LastJSDocTagNode = JSDocTypeLiteral
}
export const enum NodeFlags {
@ -416,6 +423,7 @@ namespace ts {
ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn,
EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions,
ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags,
// Parsing context flags
ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile,
@ -448,7 +456,7 @@ namespace ts {
modifiers?: ModifiersArray; // Array of modifiers
/* @internal */ id?: number; // Unique id (used to look up NodeLinks)
parent?: Node; // Parent node (initialized by binding
/* @internal */ jsDocComment?: JSDocComment; // JSDoc for the node, if it has any. Only for .js files.
/* @internal */ jsDocComments?: JSDocComment[]; // JSDoc for the node, if it has any. Only for .js files.
/* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding)
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
@ -612,6 +620,7 @@ namespace ts {
// SyntaxKind.PropertyAssignment
// SyntaxKind.ShorthandPropertyAssignment
// SyntaxKind.EnumMember
// SyntaxKind.JSDocPropertyTag
export interface VariableLikeDeclaration extends Declaration {
propertyName?: PropertyName;
dotDotDotToken?: Node;
@ -1510,6 +1519,25 @@ namespace ts {
typeExpression: JSDocTypeExpression;
}
// @kind(SyntaxKind.JSDocTypedefTag)
export interface JSDocTypedefTag extends JSDocTag, Declaration {
name?: Identifier;
typeExpression?: JSDocTypeExpression;
jsDocTypeLiteral?: JSDocTypeLiteral;
}
// @kind(SyntaxKind.JSDocPropertyTag)
export interface JSDocPropertyTag extends JSDocTag, TypeElement {
name: Identifier;
typeExpression: JSDocTypeExpression;
}
// @kind(SyntaxKind.JSDocTypeLiteral)
export interface JSDocTypeLiteral extends JSDocType {
jsDocPropertyTags?: NodeArray<JSDocPropertyTag>;
jsDocTypeTag?: JSDocTypeTag;
}
// @kind(SyntaxKind.JSDocParameterTag)
export interface JSDocParameterTag extends JSDocTag {
preParameterName?: Identifier;
@ -1537,6 +1565,13 @@ namespace ts {
id?: number; // Node id used by flow type cache in checker
}
// FlowStart represents the start of a control flow. For a function expression or arrow
// function, the container property references the function (which in turn has a flowNode
// property for the containing control flow).
export interface FlowStart extends FlowNode {
container?: FunctionExpression | ArrowFunction;
}
// FlowLabel represents a junction with multiple possible preceding control flows.
export interface FlowLabel extends FlowNode {
antecedents: FlowNode[];
@ -2162,7 +2197,7 @@ namespace ts {
/* @internal */
FreshObjectLiteral = 0x00100000, // Fresh object literal type
/* @internal */
ContainsUndefinedOrNull = 0x00200000, // Type is or contains undefined or null type
ContainsWideningType = 0x00200000, // Type is or contains undefined or null widening type
/* @internal */
ContainsObjectLiteral = 0x00400000, // Type is or contains object literal type
/* @internal */
@ -2185,9 +2220,9 @@ namespace ts {
StructuredType = ObjectType | Union | Intersection,
Narrowable = Any | ObjectType | Union | TypeParameter,
/* @internal */
RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral,
RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
/* @internal */
PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType
PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | ContainsAnyFunctionType
}
export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression;
@ -2875,4 +2910,9 @@ namespace ts {
/* @internal */ reattachFileDiagnostics(newFile: SourceFile): void;
}
// SyntaxKind.SyntaxList
export interface SyntaxList extends Node {
_children: Node[];
}
}

View file

@ -287,16 +287,36 @@ namespace ts {
return !nodeIsMissing(node);
}
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile): number {
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile, includeJsDocComment?: boolean): number {
// With nodes that have no width (i.e. 'Missing' nodes), we actually *don't*
// want to skip trivia because this will launch us forward to the next token.
if (nodeIsMissing(node)) {
return node.pos;
}
if (isJSDocNode(node)) {
return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true);
}
if (includeJsDocComment && node.jsDocComments && node.jsDocComments.length > 0) {
return getTokenPosOfNode(node.jsDocComments[0]);
}
// For a syntax list, it is possible that one of its children has JSDocComment nodes, while
// the syntax list itself considers them as normal trivia. Therefore if we simply skip
// trivia for the list, we may have skipped the JSDocComment as well. So we should process its
// first child to determine the actual position of its first token.
if (node.kind === SyntaxKind.SyntaxList && (<SyntaxList>node)._children.length > 0) {
return getTokenPosOfNode((<SyntaxList>node)._children[0], sourceFile, includeJsDocComment);
}
return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos);
}
export function isJSDocNode(node: Node) {
return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode;
}
export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFile): number {
if (nodeIsMissing(node) || !node.decorators) {
return getTokenPosOfNode(node, sourceFile);
@ -987,6 +1007,20 @@ namespace ts {
}
}
export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression {
if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) {
let prev = func;
let parent = func.parent;
while (parent.kind === SyntaxKind.ParenthesizedExpression) {
prev = parent;
parent = parent.parent;
}
if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) {
return parent as CallExpression;
}
}
}
/**
* Determines whether a node is a property or element access expression for super.
*/
@ -1322,21 +1356,23 @@ namespace ts {
return undefined;
}
const jsDocComment = getJSDocComment(node, checkParentVariableStatement);
if (!jsDocComment) {
const jsDocComments = getJSDocComments(node, checkParentVariableStatement);
if (!jsDocComments) {
return undefined;
}
for (const tag of jsDocComment.tags) {
if (tag.kind === kind) {
return tag;
for (const jsDocComment of jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.kind === kind) {
return tag;
}
}
}
}
function getJSDocComment(node: Node, checkParentVariableStatement: boolean): JSDocComment {
if (node.jsDocComment) {
return node.jsDocComment;
function getJSDocComments(node: Node, checkParentVariableStatement: boolean): JSDocComment[] {
if (node.jsDocComments) {
return node.jsDocComments;
}
// Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement.
// /**
@ -1352,7 +1388,7 @@ namespace ts {
const variableStatementNode = isInitializerOfVariableDeclarationInStatement ? node.parent.parent.parent : undefined;
if (variableStatementNode) {
return variableStatementNode.jsDocComment;
return variableStatementNode.jsDocComments;
}
// Also recognize when the node is the RHS of an assignment expression
@ -1363,12 +1399,12 @@ namespace ts {
(parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken &&
parent.parent.kind === SyntaxKind.ExpressionStatement;
if (isSourceOfAssignmentExpressionStatement) {
return parent.parent.jsDocComment;
return parent.parent.jsDocComments;
}
const isPropertyAssignmentExpression = parent && parent.kind === SyntaxKind.PropertyAssignment;
if (isPropertyAssignmentExpression) {
return parent.jsDocComment;
return parent.jsDocComments;
}
}
@ -1393,14 +1429,16 @@ namespace ts {
// annotation.
const parameterName = (<Identifier>parameter.name).text;
const jsDocComment = getJSDocComment(parameter.parent, /*checkParentVariableStatement*/ true);
if (jsDocComment) {
for (const tag of jsDocComment.tags) {
if (tag.kind === SyntaxKind.JSDocParameterTag) {
const parameterTag = <JSDocParameterTag>tag;
const name = parameterTag.preParameterName || parameterTag.postParameterName;
if (name.text === parameterName) {
return parameterTag;
const jsDocComments = getJSDocComments(parameter.parent, /*checkParentVariableStatement*/ true);
if (jsDocComments) {
for (const jsDocComment of jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.kind === SyntaxKind.JSDocParameterTag) {
const parameterTag = <JSDocParameterTag>tag;
const name = parameterTag.preParameterName || parameterTag.postParameterName;
if (name.text === parameterName) {
return parameterTag;
}
}
}
}
@ -1525,6 +1563,7 @@ namespace ts {
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.TypeParameter:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.JSDocTypedefTag:
return true;
}
return false;

View file

@ -1486,6 +1486,12 @@ namespace FourSlash {
this.fixCaretPosition();
}
public formatOnType(pos: number, key: string) {
const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeOptions);
this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true);
this.fixCaretPosition();
}
private updateMarkersForEdit(fileName: string, minChar: number, limChar: number, text: string) {
for (let i = 0; i < this.testData.markers.length; i++) {
const marker = this.testData.markers[i];
@ -3223,6 +3229,10 @@ namespace FourSlashInterface {
this.state.formatSelection(this.state.getMarkerByName(startMarker).position, this.state.getMarkerByName(endMarker).position);
}
public onType(posMarker: string, key: string) {
this.state.formatOnType(this.state.getMarkerByName(posMarker).position, key);
}
public setOption(name: string, value: number): void;
public setOption(name: string, value: string): void;
public setOption(name: string, value: boolean): void;

View file

@ -1,7 +1,7 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@ -221,6 +221,19 @@ namespace Utils {
return k;
}
// For some markers in SyntaxKind, we should print its original syntax name instead of
// the marker name in tests.
if (k === (<any>ts).SyntaxKind.FirstJSDocNode ||
k === (<any>ts).SyntaxKind.LastJSDocNode ||
k === (<any>ts).SyntaxKind.FirstJSDocTagNode ||
k === (<any>ts).SyntaxKind.LastJSDocTagNode) {
for (const kindName in (<any>ts).SyntaxKind) {
if ((<any>ts).SyntaxKind[kindName] === k) {
return kindName;
}
}
}
return (<any>ts).SyntaxKind[k];
}
@ -350,7 +363,7 @@ namespace Utils {
assert.equal(node1.end, node2.end, "node1.end !== node2.end");
assert.equal(node1.kind, node2.kind, "node1.kind !== node2.kind");
// call this on both nodes to ensure all propagated flags have been set (and thus can be
// call this on both nodes to ensure all propagated flags have been set (and thus can be
// compared).
assert.equal(ts.containsParseError(node1), ts.containsParseError(node2));
assert.equal(node1.flags, node2.flags, "node1.flags !== node2.flags");
@ -751,7 +764,7 @@ namespace Harness {
(emittedFile: string, emittedLine: number, emittedColumn: number, sourceFile: string, sourceLine: number, sourceColumn: number, sourceName: string): void;
}
// Settings
// Settings
export let userSpecifiedRoot = "";
export let lightMode = false;
@ -790,7 +803,7 @@ namespace Harness {
fileName: string,
sourceText: string,
languageVersion: ts.ScriptTarget) {
// We'll only assert invariants outside of light mode.
// We'll only assert invariants outside of light mode.
const shouldAssertInvariants = !Harness.lightMode;
// Only set the parent nodes if we're asserting invariants. We don't need them otherwise.
@ -935,7 +948,7 @@ namespace Harness {
libFiles?: string;
}
// Additional options not already in ts.optionDeclarations
// Additional options not already in ts.optionDeclarations
const harnessOptionDeclarations: ts.CommandLineOption[] = [
{ name: "allowNonTsExtensions", type: "boolean" },
{ name: "useCaseSensitiveFileNames", type: "boolean" },
@ -1187,7 +1200,7 @@ namespace Harness {
errLines.forEach(e => outputLines.push(e));
// do not count errors from lib.d.ts here, they are computed separately as numLibraryDiagnostics
// if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers)
// if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers)
// then they will be added twice thus triggering 'total errors' assertion with condition
// 'totalErrorsReportedInNonLibraryFiles + numLibraryDiagnostics + numTest262HarnessDiagnostics, diagnostics.length
@ -1497,7 +1510,7 @@ namespace Harness {
};
testUnitData.push(newTestFile2);
// unit tests always list files explicitly
// unit tests always list files explicitly
const parseConfigHost: ts.ParseConfigHost = {
readDirectory: (name) => []
};

View file

@ -1131,7 +1131,7 @@ namespace ts.server {
else {
this.log("No config files found.");
}
return {};
return configFileName ? { configFileName } : {};
}
/**

View file

@ -81,6 +81,12 @@ namespace ts.formatting {
while (isWhiteSpace(sourceFile.text.charCodeAt(endOfFormatSpan)) && !isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) {
endOfFormatSpan--;
}
// if the character at the end of the span is a line break, we shouldn't include it, because it indicates we don't want to
// touch the current line at all. Also, on some OSes the line break consists of two characters (\r\n), we should test if the
// previous character before the end of format span is line break character as well.
if (isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) {
endOfFormatSpan--;
}
const span = {
// get start position for the previous line
pos: getStartPositionOfLine(line - 1, sourceFile),

View file

@ -652,6 +652,12 @@ namespace ts.NavigationBar {
topItem.childItems.push(newItem);
}
if (node.jsDocComments && node.jsDocComments.length > 0) {
for (const jsDocComment of node.jsDocComments) {
visitNode(jsDocComment);
}
}
// Add a level if traversing into a container
if (newItem && (isFunctionLike(node) || isClassLike(node))) {
const lastTop = topItem;
@ -731,6 +737,27 @@ namespace ts.NavigationBar {
}
const declName = declarationNameToString(decl.name);
return getNavBarItem(declName, ScriptElementKind.constElement, [getNodeSpan(node)]);
case SyntaxKind.JSDocTypedefTag:
if ((<JSDocTypedefTag>node).name) {
return getNavBarItem(
(<JSDocTypedefTag>node).name.text,
ScriptElementKind.typeElement,
[getNodeSpan(node)]);
}
else {
const parentNode = node.parent && node.parent.parent;
if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) {
if ((<VariableStatement>parentNode).declarationList.declarations.length > 0) {
const nameIdentifier = (<VariableStatement>parentNode).declarationList.declarations[0].name;
if (nameIdentifier.kind === SyntaxKind.Identifier) {
return getNavBarItem(
(<Identifier>nameIdentifier).text,
ScriptElementKind.typeElement,
[getNodeSpan(node)]);
}
}
}
}
default:
return undefined;
}
@ -801,7 +828,7 @@ namespace ts.NavigationBar {
}
function getNodeSpan(node: Node) {
return node.kind === SyntaxKind.SourceFile
return node.kind === SyntaxKind.SourceFile
? createTextSpanFromBounds(node.getFullStart(), node.getEnd())
: createTextSpanFromBounds(node.getStart(), node.getEnd());
}

View file

@ -20,7 +20,7 @@ namespace ts {
getChildCount(sourceFile?: SourceFile): number;
getChildAt(index: number, sourceFile?: SourceFile): Node;
getChildren(sourceFile?: SourceFile): Node[];
getStart(sourceFile?: SourceFile): number;
getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number;
getFullStart(): number;
getEnd(): number;
getWidth(sourceFile?: SourceFile): number;
@ -172,6 +172,9 @@ namespace ts {
"static",
"throws",
"type",
"typedef",
"property",
"prop",
"version"
];
let jsDocCompletionEntries: CompletionEntry[];
@ -189,6 +192,7 @@ namespace ts {
public end: number;
public flags: NodeFlags;
public parent: Node;
public jsDocComments: JSDocComment[];
private _children: Node[];
constructor(kind: SyntaxKind, pos: number, end: number) {
@ -203,8 +207,8 @@ namespace ts {
return getSourceFileOfNode(this);
}
public getStart(sourceFile?: SourceFile): number {
return getTokenPosOfNode(this, sourceFile);
public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number {
return getTokenPosOfNode(this, sourceFile, includeJsDocComment);
}
public getFullStart(): number {
@ -235,12 +239,14 @@ namespace ts {
return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd());
}
private addSyntheticNodes(nodes: Node[], pos: number, end: number): number {
private addSyntheticNodes(nodes: Node[], pos: number, end: number, useJSDocScanner?: boolean): number {
scanner.setTextPos(pos);
while (pos < end) {
const token = scanner.scan();
const token = useJSDocScanner ? scanner.scanJSDocToken() : scanner.scan();
const textPos = scanner.getTextPos();
nodes.push(createNode(token, pos, textPos, 0, this));
if (textPos <= end) {
nodes.push(createNode(token, pos, textPos, 0, this));
}
pos = textPos;
}
return pos;
@ -270,20 +276,27 @@ namespace ts {
scanner.setText((sourceFile || this.getSourceFile()).text);
children = [];
let pos = this.pos;
const useJSDocScanner = this.kind >= SyntaxKind.FirstJSDocTagNode && this.kind <= SyntaxKind.LastJSDocTagNode;
const processNode = (node: Node) => {
if (pos < node.pos) {
pos = this.addSyntheticNodes(children, pos, node.pos);
pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner);
}
children.push(node);
pos = node.end;
};
const processNodes = (nodes: NodeArray<Node>) => {
if (pos < nodes.pos) {
pos = this.addSyntheticNodes(children, pos, nodes.pos);
pos = this.addSyntheticNodes(children, pos, nodes.pos, useJSDocScanner);
}
children.push(this.createSyntaxList(<NodeArray<Node>>nodes));
pos = nodes.end;
};
// jsDocComments need to be the first children
if (this.jsDocComments) {
for (const jsDocComment of this.jsDocComments) {
processNode(jsDocComment);
}
}
forEachChild(this, processNode, processNodes);
if (pos < this.end) {
this.addSyntheticNodes(children, pos, this.end);
@ -5637,7 +5650,7 @@ namespace ts {
const sourceFile = getValidSourceFile(fileName);
const node = getTouchingPropertyName(sourceFile, position);
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
if (node === sourceFile) {
return undefined;
}
@ -5999,7 +6012,8 @@ namespace ts {
const sourceFile = container.getSourceFile();
const tripleSlashDirectivePrefixRegex = /^\/\/\/\s*</;
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, container.getStart(), container.getEnd());
const start = findInComments ? container.getFullStart() : container.getStart();
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, start, container.getEnd());
if (possiblePositions.length) {
// Build the set of symbols to search for, initially it has only the current symbol
@ -7829,7 +7843,7 @@ namespace ts {
const defaultLibFileName = host.getDefaultLibFileName(host.getCompilationSettings());
const canonicalDefaultLibName = getCanonicalFileName(ts.normalizePath(defaultLibFileName));
const node = getTouchingWord(sourceFile, position);
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true);
// Can only rename an identifier.
if (node) {
@ -7999,6 +8013,11 @@ namespace ts {
break;
default:
forEachChild(node, walk);
if (node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
forEachChild(jsDocComment, walk);
}
}
}
}
}

View file

@ -234,29 +234,29 @@ namespace ts {
/* Gets the token whose text has range [start, end) and
* position >= start and (position < end or (position === end && token is keyword or identifier))
*/
export function getTouchingWord(sourceFile: SourceFile, position: number): Node {
return getTouchingToken(sourceFile, position, n => isWord(n.kind));
export function getTouchingWord(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node {
return getTouchingToken(sourceFile, position, n => isWord(n.kind), includeJsDocComment);
}
/* Gets the token whose text has range [start, end) and position >= start
* and (position < end or (position === end && token is keyword or identifier or numeric/string literal))
*/
export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node {
return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind));
export function getTouchingPropertyName(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node {
return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind), includeJsDocComment);
}
/** Returns the token if position is in [start, end) or if position === end and includeItemAtEndPosition(token) === true */
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition);
export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean, includeJsDocComment = false): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition, includeJsDocComment);
}
/** Returns a token if position is in [start-of-leading-trivia, end) */
export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined);
export function getTokenAtPosition(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node {
return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined, includeJsDocComment);
}
/** Get the token whose text contains the position */
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean): Node {
function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean, includeJsDocComment = false): Node {
let current: Node = sourceFile;
outer: while (true) {
if (isToken(current)) {
@ -264,13 +264,34 @@ namespace ts {
return current;
}
if (includeJsDocComment) {
const jsDocChildren = ts.filter(current.getChildren(), isJSDocNode);
for (const jsDocChild of jsDocChildren) {
const start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment);
if (start <= position) {
const end = jsDocChild.getEnd();
if (position < end || (position === end && jsDocChild.kind === SyntaxKind.EndOfFileToken)) {
current = jsDocChild;
continue outer;
}
else if (includeItemAtEndPosition && end === position) {
const previousToken = findPrecedingToken(position, sourceFile, jsDocChild);
if (previousToken && includeItemAtEndPosition(previousToken)) {
return previousToken;
}
}
}
}
}
// find the child that contains 'position'
for (let i = 0, n = current.getChildCount(sourceFile); i < n; i++) {
const child = current.getChildAt(i);
if (position < child.getFullStart() || position > child.getEnd()) {
// all jsDocComment nodes were already visited
if (isJSDocNode(child)) {
continue;
}
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile);
const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, includeJsDocComment);
if (start <= position) {
const end = child.getEnd();
if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) {
@ -285,6 +306,7 @@ namespace ts {
}
}
}
return current;
}
}
@ -423,6 +445,10 @@ namespace ts {
return false;
}
if (token.kind === SyntaxKind.JsxText) {
return true;
}
// <div>Hello |</div>
if (token.kind === SyntaxKind.LessThanToken && token.parent.kind === SyntaxKind.JsxText) {
return true;
@ -433,7 +459,7 @@ namespace ts {
return true;
}
// <div> {
// <div> {
// |
// } < /div>
if (token && token.kind === SyntaxKind.CloseBraceToken && token.parent.kind === SyntaxKind.JsxExpression) {
@ -518,11 +544,12 @@ namespace ts {
}
if (node) {
const jsDocComment = node.jsDocComment;
if (jsDocComment) {
for (const tag of jsDocComment.tags) {
if (tag.pos <= position && position <= tag.end) {
return tag;
if (node.jsDocComments) {
for (const jsDocComment of node.jsDocComments) {
for (const tag of jsDocComment.tags) {
if (tag.pos <= position && position <= tag.end) {
return tag;
}
}
}
}
@ -646,7 +673,7 @@ namespace ts {
// [a, b, c] of
// [x, [a, b, c] ] = someExpression
// or
// or
// {x, a: {a, b, c} } = someExpression
if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.kind === SyntaxKind.PropertyAssignment ? node.parent.parent : node.parent)) {
return true;

View file

@ -2,6 +2,7 @@
// array literals are widened upon assignment according to their element type
var a = []; // any[]
var a = [,,];
var a = [null, null];
var a = [undefined, undefined];
@ -12,11 +13,20 @@ var b = [[undefined, undefined]];
var c = [[[]]]; // any[][][]
var c = [[[null]],[undefined]]
// no widening when one or more elements are non-widening
var x: undefined = undefined;
var d = [x];
var d = [, x];
var d = [undefined, x];
//// [arrayLiteralWidened.js]
// array literals are widened upon assignment according to their element type
var a = []; // any[]
var a = [, ,];
var a = [null, null];
var a = [undefined, undefined];
var b = [[], [null, null]]; // any[][]
@ -24,3 +34,8 @@ var b = [[], []];
var b = [[undefined, undefined]];
var c = [[[]]]; // any[][][]
var c = [[[null]], [undefined]];
// no widening when one or more elements are non-widening
var x = undefined;
var d = [x];
var d = [, x];
var d = [undefined, x];

View file

@ -2,31 +2,53 @@
// array literals are widened upon assignment according to their element type
var a = []; // any[]
>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
var a = [,,];
>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
var a = [null, null];
>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
var a = [undefined, undefined];
>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
>undefined : Symbol(undefined)
>undefined : Symbol(undefined)
var b = [[], [null, null]]; // any[][]
>b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
var b = [[], []];
>b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
var b = [[undefined, undefined]];
>b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
>undefined : Symbol(undefined)
>undefined : Symbol(undefined)
var c = [[[]]]; // any[][][]
>c : Symbol(c, Decl(arrayLiteralWidened.ts, 11, 3), Decl(arrayLiteralWidened.ts, 12, 3))
>c : Symbol(c, Decl(arrayLiteralWidened.ts, 12, 3), Decl(arrayLiteralWidened.ts, 13, 3))
var c = [[[null]],[undefined]]
>c : Symbol(c, Decl(arrayLiteralWidened.ts, 11, 3), Decl(arrayLiteralWidened.ts, 12, 3))
>c : Symbol(c, Decl(arrayLiteralWidened.ts, 12, 3), Decl(arrayLiteralWidened.ts, 13, 3))
>undefined : Symbol(undefined)
// no widening when one or more elements are non-widening
var x: undefined = undefined;
>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
>undefined : Symbol(undefined)
var d = [x];
>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
var d = [, x];
>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
var d = [undefined, x];
>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
>undefined : Symbol(undefined)
>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))

View file

@ -5,6 +5,12 @@ var a = []; // any[]
>a : any[]
>[] : undefined[]
var a = [,,];
>a : any[]
>[,,] : undefined[]
> : undefined
> : undefined
var a = [null, null];
>a : any[]
>[null, null] : null[]
@ -53,3 +59,26 @@ var c = [[[null]],[undefined]]
>[undefined] : undefined[]
>undefined : undefined
// no widening when one or more elements are non-widening
var x: undefined = undefined;
>x : undefined
>undefined : undefined
var d = [x];
>d : undefined[]
>[x] : undefined[]
>x : undefined
var d = [, x];
>d : undefined[]
>[, x] : undefined[]
> : undefined
>x : undefined
var d = [undefined, x];
>d : undefined[]
>[undefined, x] : undefined[]
>undefined : undefined
>x : undefined

View file

@ -0,0 +1,73 @@
//// [constLocalsInFunctionExpressions.ts]
declare function getStringOrNumber(): string | number;
function f1() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = () => x.length;
}
}
function f2() {
const x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
const f = () => x.length;
}
function f3() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = function() { return x.length; };
}
}
function f4() {
const x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
const f = function() { return x.length; };
}
function f5() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = () => () => x.length;
}
}
//// [constLocalsInFunctionExpressions.js]
function f1() {
var x = getStringOrNumber();
if (typeof x === "string") {
var f = function () { return x.length; };
}
}
function f2() {
var x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
var f = function () { return x.length; };
}
function f3() {
var x = getStringOrNumber();
if (typeof x === "string") {
var f = function () { return x.length; };
}
}
function f4() {
var x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
var f = function () { return x.length; };
}
function f5() {
var x = getStringOrNumber();
if (typeof x === "string") {
var f = function () { return function () { return x.length; }; };
}
}

View file

@ -0,0 +1,95 @@
=== tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts ===
declare function getStringOrNumber(): string | number;
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
function f1() {
>f1 : Symbol(f1, Decl(constLocalsInFunctionExpressions.ts, 0, 54))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9))
const f = () => x.length;
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 5, 13))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
}
function f2() {
>f2 : Symbol(f2, Decl(constLocalsInFunctionExpressions.ts, 7, 1))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x !== "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9))
return;
}
const f = () => x.length;
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 14, 9))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
function f3() {
>f3 : Symbol(f3, Decl(constLocalsInFunctionExpressions.ts, 15, 1))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9))
const f = function() { return x.length; };
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 20, 13))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
}
function f4() {
>f4 : Symbol(f4, Decl(constLocalsInFunctionExpressions.ts, 22, 1))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x !== "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9))
return;
}
const f = function() { return x.length; };
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 29, 9))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
function f5() {
>f5 : Symbol(f5, Decl(constLocalsInFunctionExpressions.ts, 30, 1))
const x = getStringOrNumber();
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9))
const f = () => () => x.length;
>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 35, 13))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}
}

View file

@ -0,0 +1,121 @@
=== tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts ===
declare function getStringOrNumber(): string | number;
>getStringOrNumber : () => string | number
function f1() {
>f1 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
const f = () => x.length;
>f : () => number
>() => x.length : () => number
>x.length : number
>x : string
>length : number
}
}
function f2() {
>f2 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x !== "string") {
>typeof x !== "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
return;
}
const f = () => x.length;
>f : () => number
>() => x.length : () => number
>x.length : number
>x : string
>length : number
}
function f3() {
>f3 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
const f = function() { return x.length; };
>f : () => number
>function() { return x.length; } : () => number
>x.length : number
>x : string
>length : number
}
}
function f4() {
>f4 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x !== "string") {
>typeof x !== "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
return;
}
const f = function() { return x.length; };
>f : () => number
>function() { return x.length; } : () => number
>x.length : number
>x : string
>length : number
}
function f5() {
>f5 : () => void
const x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
const f = () => () => x.length;
>f : () => () => number
>() => () => x.length : () => () => number
>() => x.length : () => number
>x.length : number
>x : string
>length : number
}
}

View file

@ -0,0 +1,89 @@
//// [controlFlowIIFE.ts]
declare function getStringOrNumber(): string | number;
function f1() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = function() {
return x.length;
}();
}
}
function f2() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = (function() {
return x.length;
})();
}
}
function f3() {
let x = getStringOrNumber();
let y: number;
if (typeof x === "string") {
let n = (z => x.length + y + z)(y = 1);
}
}
// Repros from #8381
let maybeNumber: number | undefined;
(function () {
maybeNumber = 1;
})();
maybeNumber++;
if (maybeNumber !== undefined) {
maybeNumber++;
}
let test: string | undefined;
if (!test) {
throw new Error('Test is not defined');
}
(() => {
test.slice(1); // No error
})();
//// [controlFlowIIFE.js]
function f1() {
var x = getStringOrNumber();
if (typeof x === "string") {
var n = function () {
return x.length;
}();
}
}
function f2() {
var x = getStringOrNumber();
if (typeof x === "string") {
var n = (function () {
return x.length;
})();
}
}
function f3() {
var x = getStringOrNumber();
var y;
if (typeof x === "string") {
var n = (function (z) { return x.length + y + z; })(y = 1);
}
}
// Repros from #8381
var maybeNumber;
(function () {
maybeNumber = 1;
})();
maybeNumber++;
if (maybeNumber !== undefined) {
maybeNumber++;
}
var test;
if (!test) {
throw new Error('Test is not defined');
}
(function () {
test.slice(1); // No error
})();

View file

@ -0,0 +1,111 @@
=== tests/cases/conformance/controlFlow/controlFlowIIFE.ts ===
declare function getStringOrNumber(): string | number;
>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0))
function f1() {
>f1 : Symbol(f1, Decl(controlFlowIIFE.ts, 1, 54))
let x = getStringOrNumber();
>x : Symbol(x, Decl(controlFlowIIFE.ts, 4, 7))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(controlFlowIIFE.ts, 4, 7))
let n = function() {
>n : Symbol(n, Decl(controlFlowIIFE.ts, 6, 11))
return x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIIFE.ts, 4, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
}();
}
}
function f2() {
>f2 : Symbol(f2, Decl(controlFlowIIFE.ts, 10, 1))
let x = getStringOrNumber();
>x : Symbol(x, Decl(controlFlowIIFE.ts, 13, 7))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0))
if (typeof x === "string") {
>x : Symbol(x, Decl(controlFlowIIFE.ts, 13, 7))
let n = (function() {
>n : Symbol(n, Decl(controlFlowIIFE.ts, 15, 11))
return x.length;
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIIFE.ts, 13, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
})();
}
}
function f3() {
>f3 : Symbol(f3, Decl(controlFlowIIFE.ts, 19, 1))
let x = getStringOrNumber();
>x : Symbol(x, Decl(controlFlowIIFE.ts, 22, 7))
>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0))
let y: number;
>y : Symbol(y, Decl(controlFlowIIFE.ts, 23, 7))
if (typeof x === "string") {
>x : Symbol(x, Decl(controlFlowIIFE.ts, 22, 7))
let n = (z => x.length + y + z)(y = 1);
>n : Symbol(n, Decl(controlFlowIIFE.ts, 25, 11))
>z : Symbol(z, Decl(controlFlowIIFE.ts, 25, 17))
>x.length : Symbol(String.length, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(controlFlowIIFE.ts, 22, 7))
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
>y : Symbol(y, Decl(controlFlowIIFE.ts, 23, 7))
>z : Symbol(z, Decl(controlFlowIIFE.ts, 25, 17))
>y : Symbol(y, Decl(controlFlowIIFE.ts, 23, 7))
}
}
// Repros from #8381
let maybeNumber: number | undefined;
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
(function () {
maybeNumber = 1;
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
})();
maybeNumber++;
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
if (maybeNumber !== undefined) {
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
>undefined : Symbol(undefined)
maybeNumber++;
>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3))
}
let test: string | undefined;
>test : Symbol(test, Decl(controlFlowIIFE.ts, 40, 3))
if (!test) {
>test : Symbol(test, Decl(controlFlowIIFE.ts, 40, 3))
throw new Error('Test is not defined');
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
}
(() => {
test.slice(1); // No error
>test.slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
>test : Symbol(test, Decl(controlFlowIIFE.ts, 40, 3))
>slice : Symbol(String.slice, Decl(lib.d.ts, --, --))
})();

View file

@ -0,0 +1,153 @@
=== tests/cases/conformance/controlFlow/controlFlowIIFE.ts ===
declare function getStringOrNumber(): string | number;
>getStringOrNumber : () => string | number
function f1() {
>f1 : () => void
let x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
let n = function() {
>n : number
>function() { return x.length; }() : number
>function() { return x.length; } : () => number
return x.length;
>x.length : number
>x : string
>length : number
}();
}
}
function f2() {
>f2 : () => void
let x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
let n = (function() {
>n : number
>(function() { return x.length; })() : number
>(function() { return x.length; }) : () => number
>function() { return x.length; } : () => number
return x.length;
>x.length : number
>x : string
>length : number
})();
}
}
function f3() {
>f3 : () => void
let x = getStringOrNumber();
>x : string | number
>getStringOrNumber() : string | number
>getStringOrNumber : () => string | number
let y: number;
>y : number
if (typeof x === "string") {
>typeof x === "string" : boolean
>typeof x : string
>x : string | number
>"string" : string
let n = (z => x.length + y + z)(y = 1);
>n : number
>(z => x.length + y + z)(y = 1) : number
>(z => x.length + y + z) : (z: number) => number
>z => x.length + y + z : (z: number) => number
>z : number
>x.length + y + z : number
>x.length + y : number
>x.length : number
>x : string
>length : number
>y : number
>z : number
>y = 1 : number
>y : number
>1 : number
}
}
// Repros from #8381
let maybeNumber: number | undefined;
>maybeNumber : number | undefined
(function () {
>(function () { maybeNumber = 1;})() : void
>(function () { maybeNumber = 1;}) : () => void
>function () { maybeNumber = 1;} : () => void
maybeNumber = 1;
>maybeNumber = 1 : number
>maybeNumber : number | undefined
>1 : number
})();
maybeNumber++;
>maybeNumber++ : number
>maybeNumber : number
if (maybeNumber !== undefined) {
>maybeNumber !== undefined : boolean
>maybeNumber : number
>undefined : undefined
maybeNumber++;
>maybeNumber++ : number
>maybeNumber : number
}
let test: string | undefined;
>test : string | undefined
if (!test) {
>!test : boolean
>test : string | undefined
throw new Error('Test is not defined');
>new Error('Test is not defined') : Error
>Error : ErrorConstructor
>'Test is not defined' : string
}
(() => {
>(() => { test.slice(1); // No error})() : void
>(() => { test.slice(1); // No error}) : () => void
>() => { test.slice(1); // No error} : () => void
test.slice(1); // No error
>test.slice(1) : string
>test.slice : (start?: number | undefined, end?: number | undefined) => string
>test : string
>slice : (start?: number | undefined, end?: number | undefined) => string
>1 : number
})();

View file

@ -0,0 +1,291 @@
//// [controlFlowPropertyDeclarations.ts]
// Repro from ##8913
declare var require:any;
var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
// Populate property map with ReactJS's attribute and property mappings
// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
for (var propname in HTMLDOMPropertyConfig.Properties) {
if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
continue;
}
var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
}
/**
* Repeats a string a certain number of times.
* Also: the future is bright and consists of native string repetition:
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
*
* @param {string} string String to repeat
* @param {number} times Number of times to repeat string. Integer.
* @see http://jsperf.com/string-repeater/2
*/
function repeatString(string, times) {
if (times === 1) {
return string;
}
if (times < 0) { throw new Error(); }
var repeated = '';
while (times) {
if (times & 1) {
repeated += string;
}
if (times >>= 1) {
string += string;
}
}
return repeated;
}
/**
* Determine if the string ends with the specified substring.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {boolean}
*/
function endsWith(haystack, needle) {
return haystack.slice(-needle.length) === needle;
}
/**
* Trim the specified substring off the string. If the string does not end
* with the specified substring, this is a no-op.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {string}
*/
function trimEnd(haystack, needle) {
return endsWith(haystack, needle)
? haystack.slice(0, -needle.length)
: haystack;
}
/**
* Convert a hyphenated string to camelCase.
*/
function hyphenToCamelCase(string) {
return string.replace(/-(.)/g, function(match, chr) {
return chr.toUpperCase();
});
}
/**
* Determines if the specified string consists entirely of whitespace.
*/
function isEmpty(string) {
return !/[^\s]/.test(string);
}
/**
* Determines if the CSS value can be converted from a
* 'px' suffixed string to a numeric value
*
* @param {string} value CSS property value
* @return {boolean}
*/
function isConvertiblePixelValue(value) {
return /^\d+px$/.test(value);
}
export class HTMLtoJSX {
private output: string;
private level: number;
private _inPreTag: boolean;
/**
* Handles processing of the specified text node
*
* @param {TextNode} node
*/
_visitText = (node) => {
var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
if (parentTag === 'textarea' || parentTag === 'style') {
// Ignore text content of textareas and styles, as it will have already been moved
// to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
return;
}
var text = ''
if (this._inPreTag) {
// If this text is contained within a <pre>, we need to ensure the JSX
// whitespace coalescing rules don't eat the whitespace. This means
// wrapping newlines and sequences of two or more spaces in variables.
text = text
.replace(/\r/g, '')
.replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
return '{' + JSON.stringify(whitespace) + '}';
});
} else {
// If there's a newline in the text, adjust the indent level
if (text.indexOf('\n') > -1) {
}
}
this.output += text;
}
};
/**
* Handles parsing of inline styles
*/
export class StyleParser {
styles = {};
toJSXString = () => {
for (var key in this.styles) {
if (!this.styles.hasOwnProperty(key)) {
}
}
}
}
//// [controlFlowPropertyDeclarations.js]
// Repro from ##8913
"use strict";
var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
// Populate property map with ReactJS's attribute and property mappings
// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
for (var propname in HTMLDOMPropertyConfig.Properties) {
if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
continue;
}
var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
}
/**
* Repeats a string a certain number of times.
* Also: the future is bright and consists of native string repetition:
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
*
* @param {string} string String to repeat
* @param {number} times Number of times to repeat string. Integer.
* @see http://jsperf.com/string-repeater/2
*/
function repeatString(string, times) {
if (times === 1) {
return string;
}
if (times < 0) {
throw new Error();
}
var repeated = '';
while (times) {
if (times & 1) {
repeated += string;
}
if (times >>= 1) {
string += string;
}
}
return repeated;
}
/**
* Determine if the string ends with the specified substring.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {boolean}
*/
function endsWith(haystack, needle) {
return haystack.slice(-needle.length) === needle;
}
/**
* Trim the specified substring off the string. If the string does not end
* with the specified substring, this is a no-op.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {string}
*/
function trimEnd(haystack, needle) {
return endsWith(haystack, needle)
? haystack.slice(0, -needle.length)
: haystack;
}
/**
* Convert a hyphenated string to camelCase.
*/
function hyphenToCamelCase(string) {
return string.replace(/-(.)/g, function (match, chr) {
return chr.toUpperCase();
});
}
/**
* Determines if the specified string consists entirely of whitespace.
*/
function isEmpty(string) {
return !/[^\s]/.test(string);
}
/**
* Determines if the CSS value can be converted from a
* 'px' suffixed string to a numeric value
*
* @param {string} value CSS property value
* @return {boolean}
*/
function isConvertiblePixelValue(value) {
return /^\d+px$/.test(value);
}
var HTMLtoJSX = (function () {
function HTMLtoJSX() {
var _this = this;
/**
* Handles processing of the specified text node
*
* @param {TextNode} node
*/
this._visitText = function (node) {
var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
if (parentTag === 'textarea' || parentTag === 'style') {
// Ignore text content of textareas and styles, as it will have already been moved
// to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
return;
}
var text = '';
if (_this._inPreTag) {
// If this text is contained within a <pre>, we need to ensure the JSX
// whitespace coalescing rules don't eat the whitespace. This means
// wrapping newlines and sequences of two or more spaces in variables.
text = text
.replace(/\r/g, '')
.replace(/( {2,}|\n|\t|\{|\})/g, function (whitespace) {
return '{' + JSON.stringify(whitespace) + '}';
});
}
else {
// If there's a newline in the text, adjust the indent level
if (text.indexOf('\n') > -1) {
}
}
_this.output += text;
};
}
return HTMLtoJSX;
}());
exports.HTMLtoJSX = HTMLtoJSX;
;
/**
* Handles parsing of inline styles
*/
var StyleParser = (function () {
function StyleParser() {
var _this = this;
this.styles = {};
this.toJSXString = function () {
for (var key in _this.styles) {
if (!_this.styles.hasOwnProperty(key)) {
}
}
};
}
return StyleParser;
}());
exports.StyleParser = StyleParser;

View file

@ -0,0 +1,288 @@
=== tests/cases/compiler/controlFlowPropertyDeclarations.ts ===
// Repro from ##8913
declare var require:any;
>require : Symbol(require, Decl(controlFlowPropertyDeclarations.ts, 2, 11))
var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
>require : Symbol(require, Decl(controlFlowPropertyDeclarations.ts, 2, 11))
// Populate property map with ReactJS's attribute and property mappings
// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
for (var propname in HTMLDOMPropertyConfig.Properties) {
>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
continue;
}
var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
>mapFrom : Symbol(mapFrom, Decl(controlFlowPropertyDeclarations.ts, 13, 5))
>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
>propname.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
}
/**
* Repeats a string a certain number of times.
* Also: the future is bright and consists of native string repetition:
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
*
* @param {string} string String to repeat
* @param {number} times Number of times to repeat string. Integer.
* @see http://jsperf.com/string-repeater/2
*/
function repeatString(string, times) {
>repeatString : Symbol(repeatString, Decl(controlFlowPropertyDeclarations.ts, 14, 1))
>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
if (times === 1) {
>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
return string;
>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
}
if (times < 0) { throw new Error(); }
>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
var repeated = '';
>repeated : Symbol(repeated, Decl(controlFlowPropertyDeclarations.ts, 30, 5))
while (times) {
>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
if (times & 1) {
>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
repeated += string;
>repeated : Symbol(repeated, Decl(controlFlowPropertyDeclarations.ts, 30, 5))
>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
}
if (times >>= 1) {
>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
string += string;
>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
}
}
return repeated;
>repeated : Symbol(repeated, Decl(controlFlowPropertyDeclarations.ts, 30, 5))
}
/**
* Determine if the string ends with the specified substring.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {boolean}
*/
function endsWith(haystack, needle) {
>endsWith : Symbol(endsWith, Decl(controlFlowPropertyDeclarations.ts, 40, 1))
>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 49, 18))
>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 49, 27))
return haystack.slice(-needle.length) === needle;
>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 49, 18))
>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 49, 27))
>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 49, 27))
}
/**
* Trim the specified substring off the string. If the string does not end
* with the specified substring, this is a no-op.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {string}
*/
function trimEnd(haystack, needle) {
>trimEnd : Symbol(trimEnd, Decl(controlFlowPropertyDeclarations.ts, 51, 1))
>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 61, 26))
return endsWith(haystack, needle)
>endsWith : Symbol(endsWith, Decl(controlFlowPropertyDeclarations.ts, 40, 1))
>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 61, 26))
? haystack.slice(0, -needle.length)
>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 61, 26))
: haystack;
>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
}
/**
* Convert a hyphenated string to camelCase.
*/
function hyphenToCamelCase(string) {
>hyphenToCamelCase : Symbol(hyphenToCamelCase, Decl(controlFlowPropertyDeclarations.ts, 65, 1))
>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 70, 27))
return string.replace(/-(.)/g, function(match, chr) {
>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 70, 27))
>match : Symbol(match, Decl(controlFlowPropertyDeclarations.ts, 71, 42))
>chr : Symbol(chr, Decl(controlFlowPropertyDeclarations.ts, 71, 48))
return chr.toUpperCase();
>chr : Symbol(chr, Decl(controlFlowPropertyDeclarations.ts, 71, 48))
});
}
/**
* Determines if the specified string consists entirely of whitespace.
*/
function isEmpty(string) {
>isEmpty : Symbol(isEmpty, Decl(controlFlowPropertyDeclarations.ts, 74, 1))
>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 79, 17))
return !/[^\s]/.test(string);
>/[^\s]/.test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
>test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 79, 17))
}
/**
* Determines if the CSS value can be converted from a
* 'px' suffixed string to a numeric value
*
* @param {string} value CSS property value
* @return {boolean}
*/
function isConvertiblePixelValue(value) {
>isConvertiblePixelValue : Symbol(isConvertiblePixelValue, Decl(controlFlowPropertyDeclarations.ts, 81, 1))
>value : Symbol(value, Decl(controlFlowPropertyDeclarations.ts, 90, 33))
return /^\d+px$/.test(value);
>/^\d+px$/.test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
>test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
>value : Symbol(value, Decl(controlFlowPropertyDeclarations.ts, 90, 33))
}
export class HTMLtoJSX {
>HTMLtoJSX : Symbol(HTMLtoJSX, Decl(controlFlowPropertyDeclarations.ts, 92, 1))
private output: string;
>output : Symbol(HTMLtoJSX.output, Decl(controlFlowPropertyDeclarations.ts, 94, 24))
private level: number;
>level : Symbol(HTMLtoJSX.level, Decl(controlFlowPropertyDeclarations.ts, 95, 27))
private _inPreTag: boolean;
>_inPreTag : Symbol(HTMLtoJSX._inPreTag, Decl(controlFlowPropertyDeclarations.ts, 96, 26))
/**
* Handles processing of the specified text node
*
* @param {TextNode} node
*/
_visitText = (node) => {
>_visitText : Symbol(HTMLtoJSX._visitText, Decl(controlFlowPropertyDeclarations.ts, 97, 31))
>node : Symbol(node, Decl(controlFlowPropertyDeclarations.ts, 105, 16))
var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
>parentTag : Symbol(parentTag, Decl(controlFlowPropertyDeclarations.ts, 106, 7))
>node : Symbol(node, Decl(controlFlowPropertyDeclarations.ts, 105, 16))
>node : Symbol(node, Decl(controlFlowPropertyDeclarations.ts, 105, 16))
if (parentTag === 'textarea' || parentTag === 'style') {
>parentTag : Symbol(parentTag, Decl(controlFlowPropertyDeclarations.ts, 106, 7))
>parentTag : Symbol(parentTag, Decl(controlFlowPropertyDeclarations.ts, 106, 7))
// Ignore text content of textareas and styles, as it will have already been moved
// to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
return;
}
var text = ''
>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
if (this._inPreTag) {
>this._inPreTag : Symbol(HTMLtoJSX._inPreTag, Decl(controlFlowPropertyDeclarations.ts, 96, 26))
>this : Symbol(HTMLtoJSX, Decl(controlFlowPropertyDeclarations.ts, 92, 1))
>_inPreTag : Symbol(HTMLtoJSX._inPreTag, Decl(controlFlowPropertyDeclarations.ts, 96, 26))
// If this text is contained within a <pre>, we need to ensure the JSX
// whitespace coalescing rules don't eat the whitespace. This means
// wrapping newlines and sequences of two or more spaces in variables.
text = text
>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
>text .replace(/\r/g, '') .replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>text .replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
.replace(/\r/g, '')
>replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
.replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
>replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>whitespace : Symbol(whitespace, Decl(controlFlowPropertyDeclarations.ts, 121, 50))
return '{' + JSON.stringify(whitespace) + '}';
>JSON.stringify : Symbol(JSON.stringify, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>JSON : Symbol(JSON, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>stringify : Symbol(JSON.stringify, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>whitespace : Symbol(whitespace, Decl(controlFlowPropertyDeclarations.ts, 121, 50))
});
} else {
// If there's a newline in the text, adjust the indent level
if (text.indexOf('\n') > -1) {
>text.indexOf : Symbol(String.indexOf, Decl(lib.d.ts, --, --))
>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
>indexOf : Symbol(String.indexOf, Decl(lib.d.ts, --, --))
}
}
this.output += text;
>this.output : Symbol(HTMLtoJSX.output, Decl(controlFlowPropertyDeclarations.ts, 94, 24))
>this : Symbol(HTMLtoJSX, Decl(controlFlowPropertyDeclarations.ts, 92, 1))
>output : Symbol(HTMLtoJSX.output, Decl(controlFlowPropertyDeclarations.ts, 94, 24))
>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
}
};
/**
* Handles parsing of inline styles
*/
export class StyleParser {
>StyleParser : Symbol(StyleParser, Decl(controlFlowPropertyDeclarations.ts, 134, 2))
styles = {};
>styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
toJSXString = () => {
>toJSXString : Symbol(StyleParser.toJSXString, Decl(controlFlowPropertyDeclarations.ts, 140, 14))
for (var key in this.styles) {
>key : Symbol(key, Decl(controlFlowPropertyDeclarations.ts, 142, 12))
>this.styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
>this : Symbol(StyleParser, Decl(controlFlowPropertyDeclarations.ts, 134, 2))
>styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
if (!this.styles.hasOwnProperty(key)) {
>this.styles.hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
>this.styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
>this : Symbol(StyleParser, Decl(controlFlowPropertyDeclarations.ts, 134, 2))
>styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
>hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
>key : Symbol(key, Decl(controlFlowPropertyDeclarations.ts, 142, 12))
}
}
}
}

View file

@ -0,0 +1,383 @@
=== tests/cases/compiler/controlFlowPropertyDeclarations.ts ===
// Repro from ##8913
declare var require:any;
>require : any
var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
>HTMLDOMPropertyConfig : any
>require('react/lib/HTMLDOMPropertyConfig') : any
>require : any
>'react/lib/HTMLDOMPropertyConfig' : string
// Populate property map with ReactJS's attribute and property mappings
// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
for (var propname in HTMLDOMPropertyConfig.Properties) {
>propname : string
>HTMLDOMPropertyConfig.Properties : any
>HTMLDOMPropertyConfig : any
>Properties : any
if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
>!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname) : boolean
>HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname) : any
>HTMLDOMPropertyConfig.Properties.hasOwnProperty : any
>HTMLDOMPropertyConfig.Properties : any
>HTMLDOMPropertyConfig : any
>Properties : any
>hasOwnProperty : any
>propname : string
continue;
}
var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
>mapFrom : any
>HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase() : any
>HTMLDOMPropertyConfig.DOMAttributeNames[propname] : any
>HTMLDOMPropertyConfig.DOMAttributeNames : any
>HTMLDOMPropertyConfig : any
>DOMAttributeNames : any
>propname : string
>propname.toLowerCase() : string
>propname.toLowerCase : () => string
>propname : string
>toLowerCase : () => string
}
/**
* Repeats a string a certain number of times.
* Also: the future is bright and consists of native string repetition:
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
*
* @param {string} string String to repeat
* @param {number} times Number of times to repeat string. Integer.
* @see http://jsperf.com/string-repeater/2
*/
function repeatString(string, times) {
>repeatString : (string: any, times: any) => any
>string : any
>times : any
if (times === 1) {
>times === 1 : boolean
>times : any
>1 : number
return string;
>string : any
}
if (times < 0) { throw new Error(); }
>times < 0 : boolean
>times : any
>0 : number
>new Error() : Error
>Error : ErrorConstructor
var repeated = '';
>repeated : string
>'' : string
while (times) {
>times : any
if (times & 1) {
>times & 1 : number
>times : any
>1 : number
repeated += string;
>repeated += string : string
>repeated : string
>string : any
}
if (times >>= 1) {
>times >>= 1 : number
>times : any
>1 : number
string += string;
>string += string : any
>string : any
>string : any
}
}
return repeated;
>repeated : string
}
/**
* Determine if the string ends with the specified substring.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {boolean}
*/
function endsWith(haystack, needle) {
>endsWith : (haystack: any, needle: any) => boolean
>haystack : any
>needle : any
return haystack.slice(-needle.length) === needle;
>haystack.slice(-needle.length) === needle : boolean
>haystack.slice(-needle.length) : any
>haystack.slice : any
>haystack : any
>slice : any
>-needle.length : number
>needle.length : any
>needle : any
>length : any
>needle : any
}
/**
* Trim the specified substring off the string. If the string does not end
* with the specified substring, this is a no-op.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {string}
*/
function trimEnd(haystack, needle) {
>trimEnd : (haystack: any, needle: any) => any
>haystack : any
>needle : any
return endsWith(haystack, needle)
>endsWith(haystack, needle) ? haystack.slice(0, -needle.length) : haystack : any
>endsWith(haystack, needle) : boolean
>endsWith : (haystack: any, needle: any) => boolean
>haystack : any
>needle : any
? haystack.slice(0, -needle.length)
>haystack.slice(0, -needle.length) : any
>haystack.slice : any
>haystack : any
>slice : any
>0 : number
>-needle.length : number
>needle.length : any
>needle : any
>length : any
: haystack;
>haystack : any
}
/**
* Convert a hyphenated string to camelCase.
*/
function hyphenToCamelCase(string) {
>hyphenToCamelCase : (string: any) => any
>string : any
return string.replace(/-(.)/g, function(match, chr) {
>string.replace(/-(.)/g, function(match, chr) { return chr.toUpperCase(); }) : any
>string.replace : any
>string : any
>replace : any
>/-(.)/g : RegExp
>function(match, chr) { return chr.toUpperCase(); } : (match: any, chr: any) => any
>match : any
>chr : any
return chr.toUpperCase();
>chr.toUpperCase() : any
>chr.toUpperCase : any
>chr : any
>toUpperCase : any
});
}
/**
* Determines if the specified string consists entirely of whitespace.
*/
function isEmpty(string) {
>isEmpty : (string: any) => boolean
>string : any
return !/[^\s]/.test(string);
>!/[^\s]/.test(string) : boolean
>/[^\s]/.test(string) : boolean
>/[^\s]/.test : (string: string) => boolean
>/[^\s]/ : RegExp
>test : (string: string) => boolean
>string : any
}
/**
* Determines if the CSS value can be converted from a
* 'px' suffixed string to a numeric value
*
* @param {string} value CSS property value
* @return {boolean}
*/
function isConvertiblePixelValue(value) {
>isConvertiblePixelValue : (value: any) => boolean
>value : any
return /^\d+px$/.test(value);
>/^\d+px$/.test(value) : boolean
>/^\d+px$/.test : (string: string) => boolean
>/^\d+px$/ : RegExp
>test : (string: string) => boolean
>value : any
}
export class HTMLtoJSX {
>HTMLtoJSX : HTMLtoJSX
private output: string;
>output : string
private level: number;
>level : number
private _inPreTag: boolean;
>_inPreTag : boolean
/**
* Handles processing of the specified text node
*
* @param {TextNode} node
*/
_visitText = (node) => {
>_visitText : (node: any) => void
>(node) => { var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase(); if (parentTag === 'textarea' || parentTag === 'style') { // Ignore text content of textareas and styles, as it will have already been moved // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively. return; } var text = '' if (this._inPreTag) { // If this text is contained within a <pre>, we need to ensure the JSX // whitespace coalescing rules don't eat the whitespace. This means // wrapping newlines and sequences of two or more spaces in variables. text = text .replace(/\r/g, '') .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) { return '{' + JSON.stringify(whitespace) + '}'; }); } else { // If there's a newline in the text, adjust the indent level if (text.indexOf('\n') > -1) { } } this.output += text; } : (node: any) => void
>node : any
var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
>parentTag : any
>node.parentNode && node.parentNode.tagName.toLowerCase() : any
>node.parentNode : any
>node : any
>parentNode : any
>node.parentNode.tagName.toLowerCase() : any
>node.parentNode.tagName.toLowerCase : any
>node.parentNode.tagName : any
>node.parentNode : any
>node : any
>parentNode : any
>tagName : any
>toLowerCase : any
if (parentTag === 'textarea' || parentTag === 'style') {
>parentTag === 'textarea' || parentTag === 'style' : boolean
>parentTag === 'textarea' : boolean
>parentTag : any
>'textarea' : string
>parentTag === 'style' : boolean
>parentTag : any
>'style' : string
// Ignore text content of textareas and styles, as it will have already been moved
// to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
return;
}
var text = ''
>text : string
>'' : string
if (this._inPreTag) {
>this._inPreTag : boolean
>this : this
>_inPreTag : boolean
// If this text is contained within a <pre>, we need to ensure the JSX
// whitespace coalescing rules don't eat the whitespace. This means
// wrapping newlines and sequences of two or more spaces in variables.
text = text
>text = text .replace(/\r/g, '') .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) { return '{' + JSON.stringify(whitespace) + '}'; }) : string
>text : string
>text .replace(/\r/g, '') .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) { return '{' + JSON.stringify(whitespace) + '}'; }) : string
>text .replace(/\r/g, '') .replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
>text .replace(/\r/g, '') : string
>text .replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
>text : string
.replace(/\r/g, '')
>replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
>/\r/g : RegExp
>'' : string
.replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
>replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
>/( {2,}|\n|\t|\{|\})/g : RegExp
>function(whitespace) { return '{' + JSON.stringify(whitespace) + '}'; } : (whitespace: string) => string
>whitespace : string
return '{' + JSON.stringify(whitespace) + '}';
>'{' + JSON.stringify(whitespace) + '}' : string
>'{' + JSON.stringify(whitespace) : string
>'{' : string
>JSON.stringify(whitespace) : string
>JSON.stringify : { (value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[], space?: string | number): string; }
>JSON : JSON
>stringify : { (value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[], space?: string | number): string; }
>whitespace : string
>'}' : string
});
} else {
// If there's a newline in the text, adjust the indent level
if (text.indexOf('\n') > -1) {
>text.indexOf('\n') > -1 : boolean
>text.indexOf('\n') : number
>text.indexOf : (searchString: string, position?: number) => number
>text : string
>indexOf : (searchString: string, position?: number) => number
>'\n' : string
>-1 : number
>1 : number
}
}
this.output += text;
>this.output += text : string
>this.output : string
>this : this
>output : string
>text : string
}
};
/**
* Handles parsing of inline styles
*/
export class StyleParser {
>StyleParser : StyleParser
styles = {};
>styles : {}
>{} : {}
toJSXString = () => {
>toJSXString : () => void
>() => { for (var key in this.styles) { if (!this.styles.hasOwnProperty(key)) { } } } : () => void
for (var key in this.styles) {
>key : string
>this.styles : {}
>this : this
>styles : {}
if (!this.styles.hasOwnProperty(key)) {
>!this.styles.hasOwnProperty(key) : boolean
>this.styles.hasOwnProperty(key) : boolean
>this.styles.hasOwnProperty : (v: string) => boolean
>this.styles : {}
>this : this
>styles : {}
>hasOwnProperty : (v: string) => boolean
>key : string
}
}
}
}

View file

@ -1,10 +1,44 @@
//// [initializersWidened.ts]
// these are widened to any at the point of assignment
var x = null;
var y = undefined;
var x1 = null;
var y1 = undefined;
var z1 = void 0;
// these are not widened
var x2: null;
var y2: undefined;
var x3: null = null;
var y3: undefined = undefined;
var z3: undefined = void 0;
// widen only when all constituents of union are widening
var x4 = null || null;
var y4 = undefined || undefined;
var z4 = void 0 || void 0;
var x5 = null || x2;
var y5 = undefined || y2;
var z5 = void 0 || y2;
//// [initializersWidened.js]
// these are widened to any at the point of assignment
var x = null;
var y = undefined;
var x1 = null;
var y1 = undefined;
var z1 = void 0;
// these are not widened
var x2;
var y2;
var x3 = null;
var y3 = undefined;
var z3 = void 0;
// widen only when all constituents of union are widening
var x4 = null || null;
var y4 = undefined || undefined;
var z4 = void 0 || void 0;
var x5 = null || x2;
var y5 = undefined || y2;
var z5 = void 0 || y2;

View file

@ -1,10 +1,57 @@
=== tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts ===
// these are widened to any at the point of assignment
var x = null;
>x : Symbol(x, Decl(initializersWidened.ts, 2, 3))
var x1 = null;
>x1 : Symbol(x1, Decl(initializersWidened.ts, 2, 3))
var y = undefined;
>y : Symbol(y, Decl(initializersWidened.ts, 3, 3))
var y1 = undefined;
>y1 : Symbol(y1, Decl(initializersWidened.ts, 3, 3))
>undefined : Symbol(undefined)
var z1 = void 0;
>z1 : Symbol(z1, Decl(initializersWidened.ts, 4, 3))
// these are not widened
var x2: null;
>x2 : Symbol(x2, Decl(initializersWidened.ts, 8, 3))
var y2: undefined;
>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
var x3: null = null;
>x3 : Symbol(x3, Decl(initializersWidened.ts, 11, 3))
var y3: undefined = undefined;
>y3 : Symbol(y3, Decl(initializersWidened.ts, 12, 3))
>undefined : Symbol(undefined)
var z3: undefined = void 0;
>z3 : Symbol(z3, Decl(initializersWidened.ts, 13, 3))
// widen only when all constituents of union are widening
var x4 = null || null;
>x4 : Symbol(x4, Decl(initializersWidened.ts, 17, 3))
var y4 = undefined || undefined;
>y4 : Symbol(y4, Decl(initializersWidened.ts, 18, 3))
>undefined : Symbol(undefined)
>undefined : Symbol(undefined)
var z4 = void 0 || void 0;
>z4 : Symbol(z4, Decl(initializersWidened.ts, 19, 3))
var x5 = null || x2;
>x5 : Symbol(x5, Decl(initializersWidened.ts, 21, 3))
>x2 : Symbol(x2, Decl(initializersWidened.ts, 8, 3))
var y5 = undefined || y2;
>y5 : Symbol(y5, Decl(initializersWidened.ts, 22, 3))
>undefined : Symbol(undefined)
>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
var z5 = void 0 || y2;
>z5 : Symbol(z5, Decl(initializersWidened.ts, 23, 3))
>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))

View file

@ -1,11 +1,80 @@
=== tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts ===
// these are widened to any at the point of assignment
var x = null;
>x : any
var x1 = null;
>x1 : any
>null : null
var y = undefined;
>y : any
var y1 = undefined;
>y1 : any
>undefined : undefined
var z1 = void 0;
>z1 : any
>void 0 : undefined
>0 : number
// these are not widened
var x2: null;
>x2 : null
>null : null
var y2: undefined;
>y2 : undefined
var x3: null = null;
>x3 : null
>null : null
>null : null
var y3: undefined = undefined;
>y3 : undefined
>undefined : undefined
var z3: undefined = void 0;
>z3 : undefined
>void 0 : undefined
>0 : number
// widen only when all constituents of union are widening
var x4 = null || null;
>x4 : any
>null || null : null
>null : null
>null : null
var y4 = undefined || undefined;
>y4 : any
>undefined || undefined : undefined
>undefined : undefined
>undefined : undefined
var z4 = void 0 || void 0;
>z4 : any
>void 0 || void 0 : undefined
>void 0 : undefined
>0 : number
>void 0 : undefined
>0 : number
var x5 = null || x2;
>x5 : null
>null || x2 : null
>null : null
>x2 : null
var y5 = undefined || y2;
>y5 : undefined
>undefined || y2 : undefined
>undefined : undefined
>y2 : undefined
var z5 = void 0 || y2;
>z5 : undefined
>void 0 || y2 : undefined
>void 0 : undefined
>0 : number
>y2 : undefined

View file

@ -623,7 +623,7 @@ var rj8 = a8 && undefined;
var rj9 = null && undefined;
>rj9 : any
>null && undefined : null
>null && undefined : undefined
>null : null
>undefined : undefined

View file

@ -1,29 +1,61 @@
//// [objectLiteralWidened.ts]
// object literal properties are widened to any
var x = {
var x1 = {
foo: null,
bar: undefined
}
var y = {
var y1 = {
foo: null,
bar: {
baz: null,
boo: undefined
}
}
// these are not widened
var u: undefined = undefined;
var n: null = null;
var x2 = {
foo: n,
bar: u
}
var y2 = {
foo: n,
bar: {
baz: n,
boo: u
}
}
//// [objectLiteralWidened.js]
// object literal properties are widened to any
var x = {
var x1 = {
foo: null,
bar: undefined
};
var y = {
var y1 = {
foo: null,
bar: {
baz: null,
boo: undefined
}
};
// these are not widened
var u = undefined;
var n = null;
var x2 = {
foo: n,
bar: u
};
var y2 = {
foo: n,
bar: {
baz: n,
boo: u
}
};

View file

@ -1,22 +1,22 @@
=== tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts ===
// object literal properties are widened to any
var x = {
>x : Symbol(x, Decl(objectLiteralWidened.ts, 2, 3))
var x1 = {
>x1 : Symbol(x1, Decl(objectLiteralWidened.ts, 2, 3))
foo: null,
>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 2, 9))
>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 2, 10))
bar: undefined
>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 3, 14))
>undefined : Symbol(undefined)
}
var y = {
>y : Symbol(y, Decl(objectLiteralWidened.ts, 7, 3))
var y1 = {
>y1 : Symbol(y1, Decl(objectLiteralWidened.ts, 7, 3))
foo: null,
>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 7, 9))
>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 7, 10))
bar: {
>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 8, 14))
@ -29,3 +29,44 @@ var y = {
>undefined : Symbol(undefined)
}
}
// these are not widened
var u: undefined = undefined;
>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
>undefined : Symbol(undefined)
var n: null = null;
>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
var x2 = {
>x2 : Symbol(x2, Decl(objectLiteralWidened.ts, 20, 3))
foo: n,
>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 20, 10))
>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
bar: u
>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 21, 11))
>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
}
var y2 = {
>y2 : Symbol(y2, Decl(objectLiteralWidened.ts, 25, 3))
foo: n,
>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 25, 10))
>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
bar: {
>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 26, 11))
baz: n,
>baz : Symbol(baz, Decl(objectLiteralWidened.ts, 27, 10))
>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
boo: u
>boo : Symbol(boo, Decl(objectLiteralWidened.ts, 28, 15))
>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
}
}

View file

@ -1,8 +1,8 @@
=== tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts ===
// object literal properties are widened to any
var x = {
>x : { foo: any; bar: any; }
var x1 = {
>x1 : { foo: any; bar: any; }
>{ foo: null, bar: undefined} : { foo: null; bar: undefined; }
foo: null,
@ -14,8 +14,8 @@ var x = {
>undefined : undefined
}
var y = {
>y : { foo: any; bar: { baz: any; boo: any; }; }
var y1 = {
>y1 : { foo: any; bar: { baz: any; boo: any; }; }
>{ foo: null, bar: { baz: null, boo: undefined }} : { foo: null; bar: { baz: null; boo: undefined; }; }
foo: null,
@ -35,3 +35,49 @@ var y = {
>undefined : undefined
}
}
// these are not widened
var u: undefined = undefined;
>u : undefined
>undefined : undefined
var n: null = null;
>n : null
>null : null
>null : null
var x2 = {
>x2 : { foo: null; bar: undefined; }
>{ foo: n, bar: u} : { foo: null; bar: undefined; }
foo: n,
>foo : null
>n : null
bar: u
>bar : undefined
>u : undefined
}
var y2 = {
>y2 : { foo: null; bar: { baz: null; boo: undefined; }; }
>{ foo: n, bar: { baz: n, boo: u }} : { foo: null; bar: { baz: null; boo: undefined; }; }
foo: n,
>foo : null
>n : null
bar: {
>bar : { baz: null; boo: undefined; }
>{ baz: n, boo: u } : { baz: null; boo: undefined; }
baz: n,
>baz : null
>n : null
boo: u
>boo : undefined
>u : undefined
}
}

View file

@ -0,0 +1,31 @@
//// [strictNullChecksNoWidening.ts]
var a1 = null;
var a2 = undefined;
var a3 = void 0;
var b1 = [];
var b2 = [,];
var b3 = [undefined];
var b4 = [[], []];
var b5 = [[], [,]];
declare function f<T>(x: T): T;
var c1 = f(null);
var c2 = f(undefined);
var c3 = f([]);
//// [strictNullChecksNoWidening.js]
var a1 = null;
var a2 = undefined;
var a3 = void 0;
var b1 = [];
var b2 = [,];
var b3 = [undefined];
var b4 = [[], []];
var b5 = [[], [,]];
var c1 = f(null);
var c2 = f(undefined);
var c3 = f([]);

View file

@ -0,0 +1,48 @@
=== tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts ===
var a1 = null;
>a1 : Symbol(a1, Decl(strictNullChecksNoWidening.ts, 1, 3))
var a2 = undefined;
>a2 : Symbol(a2, Decl(strictNullChecksNoWidening.ts, 2, 3))
>undefined : Symbol(undefined)
var a3 = void 0;
>a3 : Symbol(a3, Decl(strictNullChecksNoWidening.ts, 3, 3))
var b1 = [];
>b1 : Symbol(b1, Decl(strictNullChecksNoWidening.ts, 5, 3))
var b2 = [,];
>b2 : Symbol(b2, Decl(strictNullChecksNoWidening.ts, 6, 3))
var b3 = [undefined];
>b3 : Symbol(b3, Decl(strictNullChecksNoWidening.ts, 7, 3))
>undefined : Symbol(undefined)
var b4 = [[], []];
>b4 : Symbol(b4, Decl(strictNullChecksNoWidening.ts, 8, 3))
var b5 = [[], [,]];
>b5 : Symbol(b5, Decl(strictNullChecksNoWidening.ts, 9, 3))
declare function f<T>(x: T): T;
>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
>x : Symbol(x, Decl(strictNullChecksNoWidening.ts, 11, 22))
>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
var c1 = f(null);
>c1 : Symbol(c1, Decl(strictNullChecksNoWidening.ts, 13, 3))
>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
var c2 = f(undefined);
>c2 : Symbol(c2, Decl(strictNullChecksNoWidening.ts, 14, 3))
>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
>undefined : Symbol(undefined)
var c3 = f([]);
>c3 : Symbol(c3, Decl(strictNullChecksNoWidening.ts, 15, 3))
>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))

View file

@ -0,0 +1,67 @@
=== tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts ===
var a1 = null;
>a1 : null
>null : null
var a2 = undefined;
>a2 : undefined
>undefined : undefined
var a3 = void 0;
>a3 : undefined
>void 0 : undefined
>0 : number
var b1 = [];
>b1 : never[]
>[] : never[]
var b2 = [,];
>b2 : undefined[]
>[,] : undefined[]
> : undefined
var b3 = [undefined];
>b3 : undefined[]
>[undefined] : undefined[]
>undefined : undefined
var b4 = [[], []];
>b4 : never[][]
>[[], []] : never[][]
>[] : never[]
>[] : never[]
var b5 = [[], [,]];
>b5 : undefined[][]
>[[], [,]] : undefined[][]
>[] : never[]
>[,] : undefined[]
> : undefined
declare function f<T>(x: T): T;
>f : <T>(x: T) => T
>T : T
>x : T
>T : T
>T : T
var c1 = f(null);
>c1 : null
>f(null) : null
>f : <T>(x: T) => T
>null : null
var c2 = f(undefined);
>c2 : undefined
>f(undefined) : undefined
>f : <T>(x: T) => T
>undefined : undefined
var c3 = f([]);
>c3 : never[]
>f([]) : never[]
>f : <T>(x: T) => T
>[] : never[]

View file

@ -41,7 +41,7 @@ function foo4(x: number | string | boolean) {
: x.toString(); // number
})(x); // x here is narrowed to number | boolean
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x: number | string | boolean) {
if (typeof x === "string") {
var y = x; // string;
@ -121,7 +121,7 @@ function foo4(x) {
: x.toString(); // number
})(x); // x here is narrowed to number | boolean
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x) {
if (typeof x === "string") {
var y = x; // string;

View file

@ -27,9 +27,9 @@ function foo(x: number | string | boolean) {
>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --))
: x.toString(); // number
>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 2, 13))
>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
} ();
}
@ -60,9 +60,9 @@ function foo2(x: number | string | boolean) {
>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --))
: x.toString(); // number
>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 12, 14))
>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
} (x); // x here is narrowed to number | boolean
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 12, 14))
@ -91,9 +91,9 @@ function foo3(x: number | string | boolean) {
>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --))
: x.toString(); // number
>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 22, 14))
>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
})();
}
@ -123,14 +123,14 @@ function foo4(x: number | string | boolean) {
>toString : Symbol(Object.toString, Decl(lib.d.ts, --, --))
: x.toString(); // number
>x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 32, 14))
>toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --))
})(x); // x here is narrowed to number | boolean
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 32, 14))
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x: number | string | boolean) {
>foo5 : Symbol(foo5, Decl(typeGuardsInFunctionAndModuleBlock.ts, 41, 1))
>x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 43, 14))

View file

@ -21,14 +21,14 @@ function foo(x: number | string | boolean) {
>f : () => string
var b = x; // number | boolean
>b : number | string | boolean
>x : number | string | boolean
>b : number | boolean
>x : number | boolean
return typeof x === "boolean"
>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string
>typeof x === "boolean" : boolean
>typeof x : string
>x : number | string | boolean
>x : number | boolean
>"boolean" : string
? x.toString() // boolean
@ -40,7 +40,7 @@ function foo(x: number | string | boolean) {
: x.toString(); // number
>x.toString() : string
>x.toString : (radix?: number) => string
>x : number | string
>x : number
>toString : (radix?: number) => string
} ();
@ -66,14 +66,14 @@ function foo2(x: number | string | boolean) {
>a : number | boolean
var b = x; // new scope - number | boolean
>b : number | string | boolean
>x : number | string | boolean
>b : number | boolean
>x : number | boolean
return typeof x === "boolean"
>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string
>typeof x === "boolean" : boolean
>typeof x : string
>x : number | string | boolean
>x : number | boolean
>"boolean" : string
? x.toString() // boolean
@ -85,7 +85,7 @@ function foo2(x: number | string | boolean) {
: x.toString(); // number
>x.toString() : string
>x.toString : (radix?: number) => string
>x : number | string
>x : number
>toString : (radix?: number) => string
} (x); // x here is narrowed to number | boolean
@ -111,14 +111,14 @@ function foo3(x: number | string | boolean) {
>() => { var b = x; // new scope - number | boolean return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number } : () => string
var b = x; // new scope - number | boolean
>b : number | string | boolean
>x : number | string | boolean
>b : number | boolean
>x : number | boolean
return typeof x === "boolean"
>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string
>typeof x === "boolean" : boolean
>typeof x : string
>x : number | string | boolean
>x : number | boolean
>"boolean" : string
? x.toString() // boolean
@ -130,7 +130,7 @@ function foo3(x: number | string | boolean) {
: x.toString(); // number
>x.toString() : string
>x.toString : (radix?: number) => string
>x : number | string
>x : number
>toString : (radix?: number) => string
})();
@ -156,14 +156,14 @@ function foo4(x: number | string | boolean) {
>a : number | boolean
var b = x; // new scope - number | boolean
>b : number | string | boolean
>x : number | string | boolean
>b : number | boolean
>x : number | boolean
return typeof x === "boolean"
>typeof x === "boolean" ? x.toString() // boolean : x.toString() : string
>typeof x === "boolean" : boolean
>typeof x : string
>x : number | string | boolean
>x : number | boolean
>"boolean" : string
? x.toString() // boolean
@ -175,13 +175,13 @@ function foo4(x: number | string | boolean) {
: x.toString(); // number
>x.toString() : string
>x.toString : (radix?: number) => string
>x : number | string
>x : number
>toString : (radix?: number) => string
})(x); // x here is narrowed to number | boolean
>x : number | boolean
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x: number | string | boolean) {
>foo5 : (x: number | string | boolean) => void
>x : number | string | boolean

View file

@ -0,0 +1,148 @@
// Repro from ##8913
declare var require:any;
var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
// Populate property map with ReactJS's attribute and property mappings
// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
for (var propname in HTMLDOMPropertyConfig.Properties) {
if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
continue;
}
var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
}
/**
* Repeats a string a certain number of times.
* Also: the future is bright and consists of native string repetition:
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
*
* @param {string} string String to repeat
* @param {number} times Number of times to repeat string. Integer.
* @see http://jsperf.com/string-repeater/2
*/
function repeatString(string, times) {
if (times === 1) {
return string;
}
if (times < 0) { throw new Error(); }
var repeated = '';
while (times) {
if (times & 1) {
repeated += string;
}
if (times >>= 1) {
string += string;
}
}
return repeated;
}
/**
* Determine if the string ends with the specified substring.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {boolean}
*/
function endsWith(haystack, needle) {
return haystack.slice(-needle.length) === needle;
}
/**
* Trim the specified substring off the string. If the string does not end
* with the specified substring, this is a no-op.
*
* @param {string} haystack String to search in
* @param {string} needle String to search for
* @return {string}
*/
function trimEnd(haystack, needle) {
return endsWith(haystack, needle)
? haystack.slice(0, -needle.length)
: haystack;
}
/**
* Convert a hyphenated string to camelCase.
*/
function hyphenToCamelCase(string) {
return string.replace(/-(.)/g, function(match, chr) {
return chr.toUpperCase();
});
}
/**
* Determines if the specified string consists entirely of whitespace.
*/
function isEmpty(string) {
return !/[^\s]/.test(string);
}
/**
* Determines if the CSS value can be converted from a
* 'px' suffixed string to a numeric value
*
* @param {string} value CSS property value
* @return {boolean}
*/
function isConvertiblePixelValue(value) {
return /^\d+px$/.test(value);
}
export class HTMLtoJSX {
private output: string;
private level: number;
private _inPreTag: boolean;
/**
* Handles processing of the specified text node
*
* @param {TextNode} node
*/
_visitText = (node) => {
var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
if (parentTag === 'textarea' || parentTag === 'style') {
// Ignore text content of textareas and styles, as it will have already been moved
// to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
return;
}
var text = ''
if (this._inPreTag) {
// If this text is contained within a <pre>, we need to ensure the JSX
// whitespace coalescing rules don't eat the whitespace. This means
// wrapping newlines and sequences of two or more spaces in variables.
text = text
.replace(/\r/g, '')
.replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
return '{' + JSON.stringify(whitespace) + '}';
});
} else {
// If there's a newline in the text, adjust the indent level
if (text.indexOf('\n') > -1) {
}
}
this.output += text;
}
};
/**
* Handles parsing of inline styles
*/
export class StyleParser {
styles = {};
toJSXString = () => {
for (var key in this.styles) {
if (!this.styles.hasOwnProperty(key)) {
}
}
}
}

View file

@ -0,0 +1,38 @@
declare function getStringOrNumber(): string | number;
function f1() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = () => x.length;
}
}
function f2() {
const x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
const f = () => x.length;
}
function f3() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = function() { return x.length; };
}
}
function f4() {
const x = getStringOrNumber();
if (typeof x !== "string") {
return;
}
const f = function() { return x.length; };
}
function f5() {
const x = getStringOrNumber();
if (typeof x === "string") {
const f = () => () => x.length;
}
}

View file

@ -0,0 +1,48 @@
// @strictNullChecks: true
declare function getStringOrNumber(): string | number;
function f1() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = function() {
return x.length;
}();
}
}
function f2() {
let x = getStringOrNumber();
if (typeof x === "string") {
let n = (function() {
return x.length;
})();
}
}
function f3() {
let x = getStringOrNumber();
let y: number;
if (typeof x === "string") {
let n = (z => x.length + y + z)(y = 1);
}
}
// Repros from #8381
let maybeNumber: number | undefined;
(function () {
maybeNumber = 1;
})();
maybeNumber++;
if (maybeNumber !== undefined) {
maybeNumber++;
}
let test: string | undefined;
if (!test) {
throw new Error('Test is not defined');
}
(() => {
test.slice(1); // No error
})();

View file

@ -40,7 +40,7 @@ function foo4(x: number | string | boolean) {
: x.toString(); // number
})(x); // x here is narrowed to number | boolean
}
// Type guards affect nested function expressions and nested function declarations
// Type guards do not affect nested function declarations
function foo5(x: number | string | boolean) {
if (typeof x === "string") {
var y = x; // string;

View file

@ -1,6 +1,7 @@
// array literals are widened upon assignment according to their element type
var a = []; // any[]
var a = [,,];
var a = [null, null];
var a = [undefined, undefined];
@ -11,3 +12,11 @@ var b = [[undefined, undefined]];
var c = [[[]]]; // any[][][]
var c = [[[null]],[undefined]]
// no widening when one or more elements are non-widening
var x: undefined = undefined;
var d = [x];
var d = [, x];
var d = [undefined, x];

View file

@ -1,4 +1,24 @@
// these are widened to any at the point of assignment
var x = null;
var y = undefined;
var x1 = null;
var y1 = undefined;
var z1 = void 0;
// these are not widened
var x2: null;
var y2: undefined;
var x3: null = null;
var y3: undefined = undefined;
var z3: undefined = void 0;
// widen only when all constituents of union are widening
var x4 = null || null;
var y4 = undefined || undefined;
var z4 = void 0 || void 0;
var x5 = null || x2;
var y5 = undefined || y2;
var z5 = void 0 || y2;

View file

@ -1,14 +1,32 @@
// object literal properties are widened to any
var x = {
var x1 = {
foo: null,
bar: undefined
}
var y = {
var y1 = {
foo: null,
bar: {
baz: null,
boo: undefined
}
}
// these are not widened
var u: undefined = undefined;
var n: null = null;
var x2 = {
foo: n,
bar: u
}
var y2 = {
foo: n,
bar: {
baz: n,
boo: u
}
}

View file

@ -0,0 +1,17 @@
// @strictNullChecks: true
var a1 = null;
var a2 = undefined;
var a3 = void 0;
var b1 = [];
var b2 = [,];
var b3 = [undefined];
var b4 = [[], []];
var b5 = [[], [,]];
declare function f<T>(x: T): T;
var c1 = f(null);
var c2 = f(undefined);
var c3 = f([]);

View file

@ -246,6 +246,7 @@ declare namespace FourSlashInterface {
copyFormatOptions(): FormatCodeOptions;
setFormatOptions(options: FormatCodeOptions): any;
selection(startMarker: string, endMarker: string): void;
onType(posMarker: string, key: string): void;
setOption(name: string, value: number): any;
setOption(name: string, value: string): any;
setOption(name: string, value: boolean): any;

View file

@ -0,0 +1,17 @@
/// <reference path="fourslash.ts"/>
/////*3*/function listAPIFiles (path : string): string[] {
//// /*1*/
//// /*2*/
////}
goTo.marker("1");
format.onType("1", "\n");
verify.currentLineContentIs(" ");
goTo.marker("2");
format.onType("2", "\n");
verify.currentLineContentIs(" ");
goTo.marker("3");
verify.currentLineContentIs("function listAPIFiles(path: string): string[] {");

View file

@ -0,0 +1,64 @@
/// <reference path="../fourslash.ts"/>
// @allowNonTsExtensions: true
// @Filename: jsdocCompletion_typedef.js
//// /** @typedef {(string | number)} NumberLike */
////
//// /**
//// * @typedef Animal
//// * @type {Object}
//// * @property {string} animalName
//// * @property {number} animalAge
//// */
////
//// /**
//// * @typedef {Object} Person
//// * @property {string} personName
//// * @property {number} personAge
//// */
////
//// /**
//// * @typedef {Object}
//// * @property {string} catName
//// * @property {number} catAge
//// */
//// var Cat;
////
//// /** @typedef {{ dogName: string, dogAge: number }} */
//// var Dog;
////
//// /** @type {NumberLike} */
//// var numberLike; numberLike./*numberLike*/
////
//// /** @type {Person} */
//// var p;p./*person*/
////
//// /** @type {Animal} */
//// var a;a./*animal*/
////
//// /** @type {Cat} */
//// var c;c./*cat*/
////
//// /** @type {Dog} */
//// var d;d./*dog*/
goTo.marker('numberLike');
verify.memberListContains('charAt');
verify.memberListContains('toExponential');
goTo.marker('person');
verify.memberListContains('personName');
verify.memberListContains('personAge');
goTo.marker('animal');
verify.memberListContains('animalName');
verify.memberListContains('animalAge');
goTo.marker('dog');
verify.memberListContains('dogName');
verify.memberListContains('dogAge');
goTo.marker('cat');
verify.memberListContains('catName');
verify.memberListContains('catAge');

View file

@ -0,0 +1,29 @@
/// <reference path="../fourslash.ts"/>
// @allowNonTsExtensions: true
// @Filename: jsdocCompletion_typedef.js
//// /**
//// * @typedef {Object} Person
//// * /*1*/@property {string} personName
//// * @property {number} personAge
//// */
////
//// /**
//// * @typedef {{ /*2*/animalName: string, animalAge: number }} Animal
//// */
////
//// /** @type {Person} */
//// var person; person.personName/*3*/
////
//// /** @type {Animal} */
//// var animal; animal.animalName/*4*/
goTo.file('jsdocCompletion_typedef.js');
goTo.marker('3');
goTo.definition();
verify.caretAtMarker('1');
goTo.marker('4');
goTo.definition();
verify.caretAtMarker('2');

View file

@ -0,0 +1,30 @@
/// <reference path="../fourslash.ts"/>
// @allowNonTsExtensions: true
// @Filename: jsDocTypedef_form2.js
////
//// /** @typedef {(string | number)} NumberLike */
//// /** @typedef {(string | number | string[])} */
//// var NumberLike2;
////
//// /** @type {/*1*/NumberLike} */
//// var numberLike;
verify.navigationBar([
{
"text": "NumberLike",
"kind": "type"
},
{
"text": "NumberLike2",
"kind": "type"
},
{
"text": "NumberLike2",
"kind": "var"
},
{
"text": "numberLike",
"kind": "var"
}
]);

View file

@ -0,0 +1,20 @@
/// <reference path="../fourslash.ts"/>
// @allowNonTsExtensions: true
// @Filename: jsDocTypedef_form1.js
////
//// /** @typedef {(string | number)} */
//// var /*1*/[|NumberLike|];
////
//// /*2*/[|NumberLike|] = 10;
////
//// /** @type {/*3*/[|NumberLike|]} */
//// var numberLike;
goTo.file('jsDocTypedef_form1.js')
goTo.marker('1');
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true);
goTo.marker('2');
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true);
goTo.marker('3');
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true);

View file

@ -0,0 +1,15 @@
/// <reference path="../fourslash.ts"/>
// @allowNonTsExtensions: true
// @Filename: jsDocTypedef_form2.js
////
//// /** @typedef {(string | number)} /*1*/[|NumberLike|] */
////
//// /** @type {/*2*/[|NumberLike|]} */
//// var numberLike;
goTo.file('jsDocTypedef_form2.js')
goTo.marker('1');
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true);
goTo.marker('2');
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true);

View file

@ -0,0 +1,20 @@
/// <reference path="../fourslash.ts"/>
// @allowNonTsExtensions: true
// @Filename: jsDocTypedef_form3.js
////
//// /**
//// * @typedef /*1*/[|Person|]
//// * @type {Object}
//// * @property {number} age
//// * @property {string} name
//// */
////
//// /** @type {/*2*/[|Person|]} */
//// var person;
goTo.file('jsDocTypedef_form3.js')
goTo.marker('1');
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true);
goTo.marker('2');
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true);

View file

@ -0,0 +1,24 @@
/// <reference path="../fourslash.ts"/>
// @allowNonTsExtensions: true
// @Filename: jsDocTypedef_form2.js
////
//// function test1() {
//// /** @typedef {(string | number)} NumberLike */
////
//// /** @type {/*1*/NumberLike} */
//// var numberLike;
//// }
//// function test2() {
//// /** @typedef {(string | number)} NumberLike2 */
////
//// /** @type {NumberLike2} */
//// var n/*2*/umberLike2;
//// }
goTo.marker('2');
verify.quickInfoExists();
goTo.marker('1');
edit.insert('111');
goTo.marker('2');
verify.quickInfoExists();

View file

@ -0,0 +1,22 @@
/// <reference path="fourslash.ts"/>
/////** @template T */
////function ident<T>: T {
////}
var c = classification;
verify.syntacticClassificationsAre(
c.comment("/** "),
c.punctuation("@"),
c.docCommentTagName("template"),
c.typeParameterName("T"),
c.comment(" */"),
c.keyword("function"),
c.identifier("ident"),
c.punctuation("<"),
c.typeParameterName("T"),
c.punctuation(">"),
c.punctuation(":"),
c.identifier("T"),
c.punctuation("{"),
c.punctuation("}"));

View file

@ -1004,7 +1004,8 @@ namespace ts {
if (result !== expected) {
// Turn on a human-readable diff
if (typeof require !== "undefined") {
require("chai").config.showDiff = true;
const chai = require("chai");
chai.config.showDiff = true;
chai.expect(JSON.parse(result)).equal(JSON.parse(expected));
}
else {
@ -2218,4 +2219,4 @@ namespace ts {
});
});
});
}
}

View file

@ -0,0 +1,294 @@
/// <reference path="..\..\..\src\harness\harness.ts" />
namespace ts {
function notImplemented(): any {
throw new Error("Not yet implemented");
}
const nullLogger: server.Logger = {
close: () => void 0,
isVerbose: () => void 0,
loggingEnabled: () => false,
perftrc: () => void 0,
info: () => void 0,
startGroup: () => void 0,
endGroup: () => void 0,
msg: () => void 0
};
const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO);
function getExecutingFilePathFromLibFile(libFile: FileOrFolder): string {
return combinePaths(getDirectoryPath(libFile.path), "tsc.js");
}
interface FileOrFolder {
path: string;
content?: string;
}
interface FSEntry {
path: Path;
fullPath: string;
}
interface File extends FSEntry {
content: string;
}
interface Folder extends FSEntry {
entries: FSEntry[];
}
function isFolder(s: FSEntry): s is Folder {
return isArray((<Folder>s).entries);
}
function isFile(s: FSEntry): s is File {
return typeof (<File>s).content === "string";
}
function addFolder(fullPath: string, toPath: (s: string) => Path, fs: FileMap<FSEntry>): Folder {
const path = toPath(fullPath);
if (fs.contains(path)) {
Debug.assert(isFolder(fs.get(path)));
return (<Folder>fs.get(path));
}
const entry: Folder = { path, entries: [], fullPath };
fs.set(path, entry);
const baseFullPath = getDirectoryPath(fullPath);
if (fullPath !== baseFullPath) {
addFolder(baseFullPath, toPath, fs).entries.push(entry);
}
return entry;
}
function sizeOfMap(map: Map<any>): number {
let n = 0;
for (const name in map) {
if (hasProperty(map, name)) {
n++;
}
}
return n;
}
function checkMapKeys(caption: string, map: Map<any>, expectedKeys: string[]) {
assert.equal(sizeOfMap(map), expectedKeys.length, `${caption}: incorrect size of map`);
for (const name of expectedKeys) {
assert.isTrue(hasProperty(map, name), `${caption} is expected to contain ${name}, actual keys: ${getKeys(map)}`);
}
}
function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) {
assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${JSON.stringify(expectedFileNames)}, got ${actualFileNames}`);
for (const f of expectedFileNames) {
assert.isTrue(contains(actualFileNames, f), `${caption}: expected to find ${f} in ${JSON.stringify(actualFileNames)}`);
}
}
function readDirectory(folder: FSEntry, ext: string, excludes: Path[], result: string[]): void {
if (!folder || !isFolder(folder) || contains(excludes, folder.path)) {
return;
}
for (const entry of folder.entries) {
if (contains(excludes, entry.path)) {
continue;
}
if (isFolder(entry)) {
readDirectory(entry, ext, excludes, result);
}
else if (fileExtensionIs(entry.path, ext)) {
result.push(entry.fullPath);
}
}
}
class TestServerHost implements server.ServerHost {
args: string[] = [];
newLine: "\n";
private fs: ts.FileMap<FSEntry>;
private getCanonicalFileName: (s: string) => string;
private toPath: (f: string) => Path;
readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {};
readonly watchedFiles: Map<FileWatcherCallback[]> = {};
constructor(public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[]) {
this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName);
this.reloadFS(fileOrFolderList);
}
reloadFS(filesOrFolders: FileOrFolder[]) {
this.fs = createFileMap<FSEntry>();
for (const fileOrFolder of filesOrFolders) {
const path = this.toPath(fileOrFolder.path);
const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory);
if (typeof fileOrFolder.content === "string") {
const entry = { path, content: fileOrFolder.content, fullPath };
this.fs.set(path, entry);
addFolder(getDirectoryPath(fullPath), this.toPath, this.fs).entries.push(entry);
}
else {
addFolder(fullPath, this.toPath, this.fs);
}
}
}
fileExists(s: string) {
const path = this.toPath(s);
return this.fs.contains(path) && isFile(this.fs.get(path));
};
directoryExists(s: string) {
const path = this.toPath(s);
return this.fs.contains(path) && isFolder(this.fs.get(path));
}
getDirectories(s: string) {
const path = this.toPath(s);
if (!this.fs.contains(path)) {
return [];
}
else {
const entry = this.fs.get(path);
return isFolder(entry) ? map(entry.entries, x => getBaseFileName(x.fullPath)) : [];
}
}
readDirectory(path: string, ext: string, excludes: string[]): string[] {
const result: string[] = [];
readDirectory(this.fs.get(this.toPath(path)), ext, map(excludes, e => toPath(e, path, this.getCanonicalFileName)), result);
return result;
}
watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher {
const path = this.toPath(directoryName);
const callbacks = lookUp(this.watchedDirectories, path) || (this.watchedDirectories[path] = []);
callbacks.push({ cb: callback, recursive });
return {
referenceCount: 0,
directoryName,
close: () => {
for (let i = 0; i < callbacks.length; i++) {
if (callbacks[i].cb === callback) {
callbacks.splice(i, 1);
break;
}
}
if (!callbacks.length) {
delete this.watchedDirectories[path];
}
}
};
}
watchFile(fileName: string, callback: FileWatcherCallback) {
const path = this.toPath(fileName);
const callbacks = lookUp(this.watchedFiles, path) || (this.watchedFiles[path] = []);
callbacks.push(callback);
return {
close: () => {
const i = callbacks.indexOf(callback);
callbacks.splice(i, 1);
if (!callbacks.length) {
delete this.watchedFiles[path];
}
}
};
}
// TOOD: record and invoke callbacks to simulate timer events
readonly setTimeout = (callback: (...args: any[]) => void, ms: number, ...args: any[]): any => void 0;
readonly clearTimeout = (timeoutId: any): void => void 0;
readonly readFile = (s: string) => (<File>this.fs.get(this.toPath(s))).content;
readonly resolvePath = (s: string) => s;
readonly getExecutingFilePath = () => this.executingFilePath;
readonly getCurrentDirectory = () => this.currentDirectory;
readonly writeFile = (path: string, content: string) => notImplemented();
readonly write = (s: string) => notImplemented();
readonly createDirectory = (s: string) => notImplemented();
readonly exit = () => notImplemented();
}
describe("tsserver project system:", () => {
it("create inferred project", () => {
const appFile: FileOrFolder = {
path: "/a/b/c/app.ts",
content: `
import {f} from "./module"
console.log(f)
`
};
const libFile: FileOrFolder = {
path: "/a/lib/lib.d.ts",
content: libFileContent
};
const moduleFile: FileOrFolder = {
path: "/a/b/c/module.d.ts",
content: `export let x: number`
};
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [appFile, moduleFile, libFile]);
const projectService = new server.ProjectService(host, nullLogger);
const { configFileName } = projectService.openClientFile(appFile.path);
assert(!configFileName, `should not find config, got: '${configFileName}`);
assert.equal(projectService.inferredProjects.length, 1, "expected one inferred project");
assert.equal(projectService.configuredProjects.length, 0, "expected no configured project");
const project = projectService.inferredProjects[0];
checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]);
checkMapKeys("watchedDirectories", host.watchedDirectories, ["/a/b/c", "/a/b", "/a"]);
});
it("create configured project without file list", () => {
const configFile: FileOrFolder = {
path: "/a/b/tsconfig.json",
content: `
{
"compilerOptions": {},
"exclude": [
"e"
]
}`
};
const libFile: FileOrFolder = {
path: "/a/lib/lib.d.ts",
content: libFileContent
};
const file1: FileOrFolder = {
path: "/a/b/c/f1.ts",
content: "let x = 1"
};
const file2: FileOrFolder = {
path: "/a/b/d/f2.ts",
content: "let y = 1"
};
const file3: FileOrFolder = {
path: "/a/b/e/f3.ts",
content: "let z = 1"
};
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [ configFile, libFile, file1, file2, file3 ]);
const projectService = new server.ProjectService(host, nullLogger);
const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
assert(configFileName, "should find config file");
assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
assert.equal(projectService.inferredProjects.length, 0, "expected no inferred project");
assert.equal(projectService.configuredProjects.length, 1, "expected one configured project");
const project = projectService.configuredProjects[0];
checkFileNames("configuredProjects project, actualFileNames", project.getFileNames(), [file1.path, libFile.path, file2.path]);
checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), [file1.path, file2.path]);
checkMapKeys("watchedFiles", host.watchedFiles, [configFile.path, file2.path, libFile.path]); // watching all files except one that was open
checkMapKeys("watchedDirectories", host.watchedDirectories, [getDirectoryPath(configFile.path)]);
});
});
}