From 06644fec2841ae6671d7e155519dc877b4eb4225 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 25 Feb 2021 17:04:41 -0800 Subject: [PATCH 1/3] Add undefined to Symbol.declarations' type Symbol.declarations now has type `Declaration[] | undefined`. I made a mistake somewhere in the checker related to JS checking, so there are quite a few test failures right now. --- src/compiler/binder.ts | 2 +- src/compiler/builderState.ts | 16 +- src/compiler/checker.ts | 614 ++++++++++-------- src/compiler/core.ts | 2 +- src/compiler/moduleSpecifiers.ts | 2 +- src/compiler/tracing.ts | 4 +- src/compiler/transformers/declarations.ts | 14 +- src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 8 +- src/harness/fourslashImpl.ts | 4 +- src/services/callHierarchy.ts | 2 +- .../codefixes/convertFunctionToEs6Class.ts | 2 +- src/services/codefixes/generateAccessors.ts | 2 +- src/services/completions.ts | 4 +- src/services/findAllReferences.ts | 4 +- src/services/getEditsForFileRename.ts | 4 +- src/services/goToDefinition.ts | 6 +- src/services/importTracker.ts | 10 +- .../convertOverloadListToSingleSignature.ts | 4 +- .../convertParamsToDestructuredObject.ts | 2 +- src/services/refactors/extractType.ts | 2 +- src/services/refactors/moveToNewFile.ts | 30 +- src/services/rename.ts | 2 +- src/services/symbolDisplay.ts | 40 +- .../reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- 26 files changed, 419 insertions(+), 367 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 946a567467d0d..28d4b52e60404 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3287,7 +3287,7 @@ namespace ts { if (node.name) { setParent(node.name, node); } - file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol))); + file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations![0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol))); } symbol.exports!.set(prototypeSymbol.escapedName, prototypeSymbol); prototypeSymbol.parent = symbol; diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 72c5c37b23382..2ef640d540616 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -166,7 +166,7 @@ namespace ts { // From ambient modules for (const ambientModule of program.getTypeChecker().getAmbientModules()) { - if (ambientModule.declarations.length > 1) { + if (ambientModule.declarations && ambientModule.declarations.length > 1) { addReferenceFromAmbientModule(ambientModule); } } @@ -174,12 +174,14 @@ namespace ts { return referencedFiles; function addReferenceFromAmbientModule(symbol: Symbol) { - // Add any file other than our own as reference - for (const declaration of symbol.declarations) { - const declarationSourceFile = getSourceFileOfNode(declaration); - if (declarationSourceFile && - declarationSourceFile !== sourceFile) { - addReferencedFile(declarationSourceFile.resolvedPath); + if (symbol.declarations) { + // Add any file other than our own as reference + for (const declaration of symbol.declarations) { + const declarationSourceFile = getSourceFileOfNode(declaration); + if (declarationSourceFile && + declarationSourceFile !== sourceFile) { + addReferencedFile(declarationSourceFile.resolvedPath); + } } } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8f159f198fbb1..3019ed19f2d80 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1241,7 +1241,7 @@ namespace ts { // as we will already report a "Declaration name conflicts..." error, and this error // won't make much sense. if (target !== globalThisSymbol) { - error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target)); + error(source.declarations && getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target)); } } else { // error @@ -1275,8 +1275,10 @@ namespace ts { return target; function addDuplicateLocations(locs: Declaration[], symbol: Symbol): void { - for (const decl of symbol.declarations) { - pushIfUnique(locs, decl); + if (symbol.declarations) { + for (const decl of symbol.declarations) { + pushIfUnique(locs, decl); + } } } } @@ -1319,11 +1321,11 @@ namespace ts { function mergeModuleAugmentation(moduleName: StringLiteral | Identifier): void { const moduleAugmentation = moduleName.parent; - if (moduleAugmentation.symbol.declarations[0] !== moduleAugmentation) { + if (moduleAugmentation.symbol.declarations?.[0] !== moduleAugmentation) { // this is a combined symbol for multiple augmentations within the same file. // its symbol already has accumulated information for all declarations // so we need to add it just once - do the work only for first declaration - Debug.assert(moduleAugmentation.symbol.declarations.length > 1); + Debug.assert(moduleAugmentation.symbol.declarations!.length > 1); return; } @@ -1822,7 +1824,7 @@ namespace ts { // ES6 exports are also visible locally (except for 'default'), but commonjs exports are not (except typedefs) if (name !== InternalSymbolName.Default && (result = lookup(moduleExports, name, meaning & SymbolFlags.ModuleMember))) { - if (isSourceFile(location) && location.commonJsModuleIndicator && !result.declarations.some(isJSDocTypeAlias)) { + if (isSourceFile(location) && location.commonJsModuleIndicator && !result.declarations?.some(isJSDocTypeAlias)) { result = undefined; } else { @@ -2202,11 +2204,13 @@ namespace ts { } function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) { - for (const decl of symbol.declarations) { - if (decl.kind === SyntaxKind.TypeParameter) { - const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent; - if (parent === container) { - return !(isJSDocTemplateTag(decl.parent) && find((decl.parent.parent as JSDoc).tags!, isJSDocTypeAlias)); // TODO: GH#18217 + if (symbol.declarations) { + for (const decl of symbol.declarations) { + if (decl.kind === SyntaxKind.TypeParameter) { + const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent; + if (parent === container) { + return !(isJSDocTemplateTag(decl.parent) && find((decl.parent.parent as JSDoc).tags!, isJSDocTypeAlias)); // TODO: GH#18217 + } } } } @@ -2405,8 +2409,7 @@ namespace ts { return; } // Block-scoped variables cannot be used before their definition - const declaration = find( - result.declarations, + const declaration = result.declarations?.find( d => isBlockOrCatchScoped(d) || isClassLike(d) || (d.kind === SyntaxKind.EnumDeclaration)); if (declaration === undefined) return Debug.fail("checkResolvedBlockScopedVariable could not find block-scoped declaration"); @@ -2462,7 +2465,7 @@ namespace ts { } function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration | undefined { - return find(symbol.declarations, isAliasSymbolDeclaration); + return symbol.declarations?.find(isAliasSymbolDeclaration); } /** @@ -2596,7 +2599,7 @@ namespace ts { exportDefaultSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, node, dontResolveAlias); } - const file = find(moduleSymbol.declarations, isSourceFile); + const file = moduleSymbol.declarations?.find(isSourceFile); const hasSyntheticDefault = canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias); if (!exportDefaultSymbol && !hasSyntheticDefault) { if (hasExportAssignmentSymbol(moduleSymbol)) { @@ -2639,7 +2642,7 @@ namespace ts { const diagnostic = error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); const exportStar = moduleSymbol.exports?.get(InternalSymbolName.ExportStar); if (exportStar) { - const defaultExport = find(exportStar.declarations, decl => !!( + const defaultExport = exportStar.declarations?.find(decl => !!( isExportDeclaration(decl) && decl.moduleSpecifier && resolveExternalModuleName(decl, decl.moduleSpecifier)?.exports?.has(InternalSymbolName.Default) )); @@ -2746,7 +2749,7 @@ namespace ts { let symbolFromModule = getExportOfModule(targetSymbol, name, specifier, dontResolveAlias); if (symbolFromModule === undefined && name.escapedText === InternalSymbolName.Default) { - const file = find(moduleSymbol.declarations, isSourceFile); + const file = moduleSymbol.declarations?.find(isSourceFile); if (canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias)) { symbolFromModule = resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias); } @@ -2800,10 +2803,11 @@ namespace ts { const exportedSymbol = exports ? find(symbolsToArray(exports), symbol => !!getSymbolIfSameReference(symbol, localSymbol)) : undefined; const diagnostic = exportedSymbol ? error(name, Diagnostics.Module_0_declares_1_locally_but_it_is_exported_as_2, moduleName, declarationName, symbolToString(exportedSymbol)) : error(name, Diagnostics.Module_0_declares_1_locally_but_it_is_not_exported, moduleName, declarationName); - - addRelatedInfo(diagnostic, - ...map(localSymbol.declarations, (decl, index) => - createDiagnosticForNode(decl, index === 0 ? Diagnostics._0_is_declared_here : Diagnostics.and_here, declarationName))); + if (localSymbol.declarations) { + addRelatedInfo(diagnostic, + ...map(localSymbol.declarations, (decl, index) => + createDiagnosticForNode(decl, index === 0 ? Diagnostics._0_is_declared_here : Diagnostics.and_here, declarationName))); + } } } else { @@ -3582,15 +3586,17 @@ namespace ts { if (exportStars) { const nestedSymbols = createSymbolTable(); const lookupTable: ExportCollisionTrackerTable = new Map(); - for (const node of exportStars.declarations) { - const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!); - const exportedSymbols = visit(resolvedModule); - extendExportSymbols( - nestedSymbols, - exportedSymbols, - lookupTable, - node as ExportDeclaration - ); + if (exportStars.declarations) { + for (const node of exportStars.declarations) { + const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!); + const exportedSymbols = visit(resolvedModule); + extendExportSymbols( + nestedSymbols, + exportedSymbols, + lookupTable, + node as ExportDeclaration + ); + } } lookupTable.forEach(({ exportsWithDuplicate }, id) => { // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself @@ -3729,7 +3735,7 @@ namespace ts { // If we're trying to reference some object literal in, eg `var a = { x: 1 }`, the symbol for the literal, `__object`, is distinct // from the symbol of the declaration it is being assigned to. Since we can use the declaration to refer to the literal, however, // we'd like to make that connection here - potentially causing us to paint the declaration's visibility, and therefore the literal. - const firstDecl: Node | false = !!length(symbol.declarations) && first(symbol.declarations); + const firstDecl: Node | false = !!length(symbol.declarations) && first(symbol.declarations!); if (meaning & SymbolFlags.Value && firstDecl && firstDecl.parent && isVariableDeclaration(firstDecl.parent)) { if (isObjectLiteralExpression(firstDecl) && firstDecl === firstDecl.parent.initializer || isTypeLiteralNode(firstDecl) && firstDecl === firstDecl.parent.type) { return getSymbolOfNode(firstDecl.parent); @@ -5114,8 +5120,8 @@ namespace ts { const saveEnclosingDeclaration = context.enclosingDeclaration; context.enclosingDeclaration = undefined; if (context.tracker.trackSymbol && getCheckFlags(propertySymbol) & CheckFlags.Late && isLateBoundName(propertySymbol.escapedName)) { - const decl = first(propertySymbol.declarations); - if (hasLateBindableName(decl)) { + const decl = first(propertySymbol.declarations!); + if (propertySymbol.declarations && hasLateBindableName(decl)) { if (isBinaryExpression(decl)) { const name = getNameOfDeclaration(decl); if (name && isElementAccessExpression(name) && isPropertyAccessEntityNameExpression(name.argumentExpression)) { @@ -5166,7 +5172,7 @@ namespace ts { function preserveCommentsOn(node: T) { if (some(propertySymbol.declarations, d => d.kind === SyntaxKind.JSDocPropertyTag)) { - const d = find(propertySymbol.declarations, d => d.kind === SyntaxKind.JSDocPropertyTag)! as JSDocPropertyTag; + const d = propertySymbol.declarations?.find(d => d.kind === SyntaxKind.JSDocPropertyTag)! as JSDocPropertyTag; const commentText = d.comment; if (commentText) { setSyntheticLeadingComments(node, [{ kind: SyntaxKind.MultiLineCommentTrivia, text: "*\n * " + commentText.replace(/\n/g, "\n * ") + "\n ", pos: -1, end: -1, hasTrailingNewLine: true }]); @@ -5588,7 +5594,7 @@ namespace ts { if (context.tracker.trackReferencedAmbientModule) { const ambientDecls = filter(symbol.declarations, isAmbientModule); if (length(ambientDecls)) { - for (const decl of ambientDecls) { + for (const decl of ambientDecls!) { context.tracker.trackReferencedAmbientModule(decl, symbol); } } @@ -6514,7 +6520,7 @@ namespace ts { if (textRange && isVariableDeclarationList(textRange.parent) && textRange.parent.declarations.length === 1) { textRange = textRange.parent.parent; } - const propertyAccessRequire = find(symbol.declarations, isPropertyAccessExpression); + const propertyAccessRequire = symbol.declarations?.find(isPropertyAccessExpression); if (propertyAccessRequire && isBinaryExpression(propertyAccessRequire.parent) && isIdentifier(propertyAccessRequire.parent.right) && type.symbol && isSourceFile(type.symbol.valueDeclaration)) { const alias = localName === propertyAccessRequire.parent.right.escapedText ? undefined : propertyAccessRequire.parent.right; @@ -6602,10 +6608,12 @@ namespace ts { if (symbol.flags & SymbolFlags.ExportStar) { // synthesize export * from "moduleReference" // Straightforward - only one thing to do - make an export declaration - for (const node of symbol.declarations) { - const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!); - if (!resolvedModule) continue; - addResult(factory.createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*isTypeOnly*/ false, /*exportClause*/ undefined, factory.createStringLiteral(getSpecifierForModuleSymbol(resolvedModule, context))), ModifierFlags.None); + if (symbol.declarations) { + for (const node of symbol.declarations) { + const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!); + if (!resolvedModule) continue; + addResult(factory.createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*isTypeOnly*/ false, /*exportClause*/ undefined, factory.createStringLiteral(getSpecifierForModuleSymbol(resolvedModule, context))), ModifierFlags.None); + } } } if (needsPostExportDefault) { @@ -6676,7 +6684,7 @@ namespace ts { const aliasType = getDeclaredTypeOfTypeAlias(symbol); const typeParams = getSymbolLinks(symbol).typeParameters; const typeParamDecls = map(typeParams, p => typeParameterToDeclaration(p, context)); - const jsdocAliasDecl = find(symbol.declarations, isJSDocTypeAlias); + const jsdocAliasDecl = symbol.declarations?.find(isJSDocTypeAlias); const commentText = jsdocAliasDecl ? jsdocAliasDecl.comment || jsdocAliasDecl.parent.comment : undefined; const oldFlags = context.flags; context.flags |= NodeBuilderFlags.InTypeAlias; @@ -6901,7 +6909,7 @@ namespace ts { } function serializeAsClass(symbol: Symbol, localName: string, modifierFlags: ModifierFlags) { - const originalDecl = find(symbol.declarations, isClassLike); + const originalDecl = symbol.declarations?.find(isClassLike); const oldEnclosing = context.enclosingDeclaration; context.enclosingDeclaration = originalDecl || oldEnclosing; const localParams = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); @@ -7324,7 +7332,7 @@ namespace ts { } const flag = (modifierFlags & ~ModifierFlags.Async) | (isStatic ? ModifierFlags.Static : 0); const name = getPropertyNameNodeForSymbol(p, context); - const firstPropertyLikeDecl = find(p.declarations, or(isPropertyDeclaration, isAccessor, isVariableDeclaration, isPropertySignature, isBinaryExpression, isPropertyAccessExpression)); + const firstPropertyLikeDecl = p.declarations?.find(or(isPropertyDeclaration, isAccessor, isVariableDeclaration, isPropertySignature, isBinaryExpression, isPropertyAccessExpression)); if (p.flags & SymbolFlags.Accessor && useAccessors) { const result: AccessorDeclaration[] = []; if (p.flags & SymbolFlags.SetAccessor) { @@ -7341,7 +7349,7 @@ namespace ts { isPrivate ? undefined : serializeTypeForDeclaration(context, getTypeOfSymbol(p), p, enclosingDeclaration, includePrivateSymbol, bundled) )], /*body*/ undefined - ), find(p.declarations, isSetAccessor) || firstPropertyLikeDecl)); + ), p.declarations?.find(isSetAccessor) || firstPropertyLikeDecl)); } if (p.flags & SymbolFlags.GetAccessor) { const isPrivate = modifierFlags & ModifierFlags.Private; @@ -7352,7 +7360,7 @@ namespace ts { [], isPrivate ? undefined : serializeTypeForDeclaration(context, getTypeOfSymbol(p), p, enclosingDeclaration, includePrivateSymbol, bundled), /*body*/ undefined - ), find(p.declarations, isGetAccessor) || firstPropertyLikeDecl)); + ), p.declarations?.find(isGetAccessor) || firstPropertyLikeDecl)); } return result; } @@ -7368,7 +7376,7 @@ namespace ts { // TODO: https://github.com/microsoft/TypeScript/pull/32372#discussion_r328386357 // interface members can't have initializers, however class members _can_ /*initializer*/ undefined - ), find(p.declarations, or(isPropertyDeclaration, isVariableDeclaration)) || firstPropertyLikeDecl); + ), p.declarations?.find(or(isPropertyDeclaration, isVariableDeclaration)) || firstPropertyLikeDecl); } if (p.flags & (SymbolFlags.Method | SymbolFlags.Function)) { const type = getTypeOfSymbol(p); @@ -7381,7 +7389,7 @@ namespace ts { p.flags & SymbolFlags.Optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined, /*type*/ undefined, /*initializer*/ undefined - ), find(p.declarations, isFunctionLikeDeclaration) || signatures[0] && signatures[0].declaration || p.declarations[0]); + ), p.declarations?.find(isFunctionLikeDeclaration) || signatures[0] && signatures[0].declaration || p.declarations && p.declarations[0]); } const results = []; @@ -7627,7 +7635,7 @@ namespace ts { } function getTypeAliasForTypeLiteral(type: Type): Symbol | undefined { - if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral) { + if (type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral && type.symbol.declarations) { const node = walkUpParenthesizedTypes(type.symbol.declarations[0].parent); if (node.kind === SyntaxKind.TypeAliasDeclaration) { return getSymbolOfNode(node); @@ -7862,7 +7870,7 @@ namespace ts { } return result; - function buildVisibleNodeList(declarations: Declaration[]) { + function buildVisibleNodeList(declarations: Declaration[] | undefined) { forEach(declarations, declaration => { const resultNode = getAnyImportSyntax(declaration) || declaration; if (setVisibility) { @@ -8352,12 +8360,12 @@ namespace ts { } function getDeclaringConstructor(symbol: Symbol) { - for (const declaration of symbol.declarations) { + return symbol.declarations?.find(declaration => { const container = getThisContainer(declaration, /*includeArrowFunctions*/ false); if (container && (container.kind === SyntaxKind.Constructor || isJSConstructor(container))) { - return container; + return container; } - } + }) as ConstructorDeclaration | undefined; } function getFlowTypeInConstructor(symbol: Symbol, constructor: ConstructorDeclaration) { @@ -8403,30 +8411,32 @@ namespace ts { if (!type) { let jsdocType: Type | undefined; let types: Type[] | undefined; - for (const declaration of symbol.declarations) { - const expression = (isBinaryExpression(declaration) || isCallExpression(declaration)) ? declaration : - isAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration : - undefined; - if (!expression) { - continue; // Non-assignment declaration merged in (eg, an Identifier to mark the thing as a namespace) - skip over it and pull type info from elsewhere - } + if (symbol.declarations) { + for (const declaration of symbol.declarations) { + const expression = (isBinaryExpression(declaration) || isCallExpression(declaration)) ? declaration : + isAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration : + undefined; + if (!expression) { + continue; // Non-assignment declaration merged in (eg, an Identifier to mark the thing as a namespace) - skip over it and pull type info from elsewhere + } - const kind = isAccessExpression(expression) - ? getAssignmentDeclarationPropertyAccessKind(expression) - : getAssignmentDeclarationKind(expression); - if (kind === AssignmentDeclarationKind.ThisProperty || isBinaryExpression(expression) && isPossiblyAliasedThisProperty(expression, kind)) { - if (isDeclarationInConstructor(expression)) { - definedInConstructor = true; + const kind = isAccessExpression(expression) + ? getAssignmentDeclarationPropertyAccessKind(expression) + : getAssignmentDeclarationKind(expression); + if (kind === AssignmentDeclarationKind.ThisProperty || isBinaryExpression(expression) && isPossiblyAliasedThisProperty(expression, kind)) { + if (isDeclarationInConstructor(expression)) { + definedInConstructor = true; + } + else { + definedInMethod = true; + } } - else { - definedInMethod = true; + if (!isCallExpression(expression)) { + jsdocType = getAnnotatedTypeForAssignmentDeclaration(jsdocType, expression, symbol, declaration); + } + if (!jsdocType) { + (types || (types = [])).push((isBinaryExpression(expression) || isCallExpression(expression)) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType); } - } - if (!isCallExpression(expression)) { - jsdocType = getAnnotatedTypeForAssignmentDeclaration(jsdocType, expression, symbol, declaration); - } - if (!jsdocType) { - (types || (types = [])).push((isBinaryExpression(expression) || isCallExpression(expression)) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType); } } type = jsdocType; @@ -8434,7 +8444,7 @@ namespace ts { if (!length(types)) { return errorType; // No types from any declarations :( } - let constructorTypes = definedInConstructor ? getConstructorDefinedThisAssignmentTypes(types!, symbol.declarations) : undefined; + let constructorTypes = definedInConstructor && symbol.declarations ? getConstructorDefinedThisAssignmentTypes(types!, symbol.declarations) : undefined; // use only the constructor types unless they were only assigned null | undefined (including widening variants) if (definedInMethod) { const propType = getTypeOfPropertyInBaseClass(symbol); @@ -9273,14 +9283,16 @@ namespace ts { // interface, or type alias. function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] | undefined { let result: TypeParameter[] | undefined; - for (const node of symbol.declarations) { - if (node.kind === SyntaxKind.InterfaceDeclaration || - node.kind === SyntaxKind.ClassDeclaration || - node.kind === SyntaxKind.ClassExpression || - isJSConstructor(node) || - isTypeAlias(node)) { - const declaration = node; - result = appendTypeParameters(result, getEffectiveTypeParameterDeclarations(declaration)); + if (symbol.declarations) { + for (const node of symbol.declarations) { + if (node.kind === SyntaxKind.InterfaceDeclaration || + node.kind === SyntaxKind.ClassDeclaration || + node.kind === SyntaxKind.ClassExpression || + isJSConstructor(node) || + isTypeAlias(node)) { + const declaration = node; + result = appendTypeParameters(result, getEffectiveTypeParameterDeclarations(declaration)); + } } } return result; @@ -9378,7 +9390,9 @@ namespace ts { ctorReturn = getReturnTypeOfSignature(ctorSig[0]); } } - addRelatedInfo(err, createDiagnosticForNode(baseConstructorType.symbol.declarations[0], Diagnostics.Did_you_mean_for_0_to_be_constrained_to_type_new_args_Colon_any_1, symbolToString(baseConstructorType.symbol), typeToString(ctorReturn))); + if (baseConstructorType.symbol.declarations) { + addRelatedInfo(err, createDiagnosticForNode(baseConstructorType.symbol.declarations[0], Diagnostics.Did_you_mean_for_0_to_be_constrained_to_type_new_args_Colon_any_1, symbolToString(baseConstructorType.symbol), typeToString(ctorReturn))); + } } return type.resolvedBaseConstructorType = errorType; } @@ -9389,17 +9403,19 @@ namespace ts { function getImplementsTypes(type: InterfaceType): BaseType[] { let resolvedImplementsTypes: BaseType[] = emptyArray; - for (const declaration of type.symbol.declarations) { - const implementsTypeNodes = getEffectiveImplementsTypeNodes(declaration as ClassLikeDeclaration); - if (!implementsTypeNodes) continue; - for (const node of implementsTypeNodes) { - const implementsType = getTypeFromTypeNode(node); - if (implementsType !== errorType) { - if (resolvedImplementsTypes === emptyArray) { - resolvedImplementsTypes = [implementsType]; - } - else { - resolvedImplementsTypes.push(implementsType); + if (type.symbol.declarations) { + for (const declaration of type.symbol.declarations) { + const implementsTypeNodes = getEffectiveImplementsTypeNodes(declaration as ClassLikeDeclaration); + if (!implementsTypeNodes) continue; + for (const node of implementsTypeNodes) { + const implementsType = getTypeFromTypeNode(node); + if (implementsType !== errorType) { + if (resolvedImplementsTypes === emptyArray) { + resolvedImplementsTypes = [implementsType]; + } + else { + resolvedImplementsTypes.push(implementsType); + } } } } @@ -9428,7 +9444,7 @@ namespace ts { else { Debug.fail("type must be class or interface"); } - if (!popTypeResolution()) { + if (!popTypeResolution() && type.symbol.declarations) { for (const declaration of type.symbol.declarations) { if (declaration.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.InterfaceDeclaration) { reportCircularBaseType(declaration, type); @@ -9530,27 +9546,29 @@ namespace ts { function resolveBaseTypesOfInterface(type: InterfaceType): void { type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; - for (const declaration of type.symbol.declarations) { - if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration)) { - for (const node of getInterfaceBaseTypeNodes(declaration)!) { - const baseType = getReducedType(getTypeFromTypeNode(node)); - if (baseType !== errorType) { - if (isValidBaseType(baseType)) { - if (type !== baseType && !hasBaseType(baseType, type)) { - if (type.resolvedBaseTypes === emptyArray) { - type.resolvedBaseTypes = [baseType]; + if (type.symbol.declarations) { + for (const declaration of type.symbol.declarations) { + if (declaration.kind === SyntaxKind.InterfaceDeclaration && getInterfaceBaseTypeNodes(declaration)) { + for (const node of getInterfaceBaseTypeNodes(declaration)!) { + const baseType = getReducedType(getTypeFromTypeNode(node)); + if (baseType !== errorType) { + if (isValidBaseType(baseType)) { + if (type !== baseType && !hasBaseType(baseType, type)) { + if (type.resolvedBaseTypes === emptyArray) { + type.resolvedBaseTypes = [baseType]; + } + else { + type.resolvedBaseTypes.push(baseType); + } } else { - type.resolvedBaseTypes.push(baseType); + reportCircularBaseType(declaration, type); } } else { - reportCircularBaseType(declaration, type); + error(node, Diagnostics.An_interface_can_only_extend_an_object_type_or_intersection_of_object_types_with_statically_known_members); } } - else { - error(node, Diagnostics.An_interface_can_only_extend_an_object_type_or_intersection_of_object_types_with_statically_known_members); - } } } } @@ -9565,18 +9583,20 @@ namespace ts { * and if none of the base interfaces have a "this" type. */ function isThislessInterface(symbol: Symbol): boolean { - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.InterfaceDeclaration) { - if (declaration.flags & NodeFlags.ContainsThis) { - return false; - } - const baseTypeNodes = getInterfaceBaseTypeNodes(declaration); - if (baseTypeNodes) { - for (const node of baseTypeNodes) { - if (isEntityNameExpression(node.expression)) { - const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); - if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { - return false; + if (symbol.declarations) { + for (const declaration of symbol.declarations) { + if (declaration.kind === SyntaxKind.InterfaceDeclaration) { + if (declaration.flags & NodeFlags.ContainsThis) { + return false; + } + const baseTypeNodes = getInterfaceBaseTypeNodes(declaration); + if (baseTypeNodes) { + for (const node of baseTypeNodes) { + if (isEntityNameExpression(node.expression)) { + const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); + if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { + return false; + } } } } @@ -9631,7 +9651,7 @@ namespace ts { return errorType; } - const declaration = Debug.checkDefined(find(symbol.declarations, isTypeAlias), "Type alias symbol with no valid declaration found"); + const declaration = Debug.checkDefined(symbol.declarations?.find(isTypeAlias), "Type alias symbol with no valid declaration found"); const typeNode = isJSDocTypeAlias(declaration) ? declaration.typeExpression : declaration.type; // If typeNode is missing, we will error in checkJSDocTypedefTag. let type = typeNode ? getTypeFromTypeNode(typeNode) : errorType; @@ -9693,14 +9713,16 @@ namespace ts { return links.enumKind; } let hasNonLiteralMember = false; - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.EnumDeclaration) { - for (const member of (declaration).members) { - if (member.initializer && isStringLiteralLike(member.initializer)) { - return links.enumKind = EnumKind.Literal; - } - if (!isLiteralEnumMember(member)) { - hasNonLiteralMember = true; + if (symbol.declarations) { + for (const declaration of symbol.declarations) { + if (declaration.kind === SyntaxKind.EnumDeclaration) { + for (const member of (declaration).members) { + if (member.initializer && isStringLiteralLike(member.initializer)) { + return links.enumKind = EnumKind.Literal; + } + if (!isLiteralEnumMember(member)) { + hasNonLiteralMember = true; + } } } } @@ -9720,13 +9742,15 @@ namespace ts { if (getEnumKind(symbol) === EnumKind.Literal) { enumCount++; const memberTypeList: Type[] = []; - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.EnumDeclaration) { - for (const member of (declaration).members) { - const value = getEnumMemberValue(member); - const memberType = getFreshTypeOfLiteralType(getLiteralType(value !== undefined ? value : 0, enumCount, getSymbolOfNode(member))); - getSymbolLinks(getSymbolOfNode(member)).declaredType = memberType; - memberTypeList.push(getRegularTypeOfLiteralType(memberType)); + if (symbol.declarations) { + for (const declaration of symbol.declarations) { + if (declaration.kind === SyntaxKind.EnumDeclaration) { + for (const member of (declaration).members) { + const value = getEnumMemberValue(member); + const memberType = getFreshTypeOfLiteralType(getLiteralType(value !== undefined ? value : 0, enumCount, getSymbolOfNode(member))); + getSymbolLinks(getSymbolOfNode(member)).declaredType = memberType; + memberTypeList.push(getRegularTypeOfLiteralType(memberType)); + } } } } @@ -12022,19 +12046,21 @@ namespace ts { function getSignaturesOfSymbol(symbol: Symbol | undefined): Signature[] { if (!symbol) return emptyArray; const result: Signature[] = []; - for (let i = 0; i < symbol.declarations.length; i++) { - const decl = symbol.declarations[i]; - if (!isFunctionLike(decl)) continue; - // Don't include signature if node is the implementation of an overloaded function. A node is considered - // an implementation node if it has a body and the previous node is of the same kind and immediately - // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). - if (i > 0 && (decl as FunctionLikeDeclaration).body) { - const previous = symbol.declarations[i - 1]; - if (decl.parent === previous.parent && decl.kind === previous.kind && decl.pos === previous.end) { - continue; + if (symbol.declarations) { + for (let i = 0; i < symbol.declarations.length; i++) { + const decl = symbol.declarations[i]; + if (!isFunctionLike(decl)) continue; + // Don't include signature if node is the implementation of an overloaded function. A node is considered + // an implementation node if it has a body and the previous node is of the same kind and immediately + // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). + if (i > 0 && (decl as FunctionLikeDeclaration).body) { + const previous = symbol.declarations[i - 1]; + if (decl.parent === previous.parent && decl.kind === previous.kind && decl.pos === previous.end) { + continue; + } } + result.push(getSignatureFromDeclaration(decl)); } - result.push(getSignatureFromDeclaration(decl)); } return result; } @@ -12288,7 +12314,7 @@ namespace ts { function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): IndexSignatureDeclaration | undefined { const syntaxKind = kind === IndexKind.Number ? SyntaxKind.NumberKeyword : SyntaxKind.StringKeyword; const indexSymbol = getIndexSymbol(symbol); - if (indexSymbol) { + if (indexSymbol?.declarations) { for (const decl of indexSymbol.declarations) { const node = cast(decl, isIndexSignatureDeclaration); if (node.parameters.length === 1) { @@ -12322,7 +12348,7 @@ namespace ts { function getInferredTypeParameterConstraint(typeParameter: TypeParameter) { let inferences: Type[] | undefined; - if (typeParameter.symbol) { + if (typeParameter.symbol?.declarations) { for (const declaration of typeParameter.symbol.declarations) { if (declaration.parent.kind === SyntaxKind.InferType) { // When an 'infer T' declaration is immediately contained in a type reference node @@ -12837,12 +12863,14 @@ namespace ts { function getTypeDeclaration(symbol: Symbol): Declaration | undefined { const declarations = symbol.declarations; - for (const declaration of declarations) { - switch (declaration.kind) { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.EnumDeclaration: - return declaration; + if (declarations) { + for (const declaration of declarations) { + switch (declaration.kind) { + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.EnumDeclaration: + return declaration; + } } } } @@ -14243,7 +14271,7 @@ namespace ts { if (propName !== undefined) { const prop = getPropertyOfType(objectType, propName); if (prop) { - if (reportDeprecated && accessNode && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) { + if (reportDeprecated && accessNode && prop.declarations && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) { const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode); addDeprecatedSuggestion(deprecatedNode, prop.declarations, propName as string); } @@ -15123,7 +15151,7 @@ namespace ts { function isSpreadableProperty(prop: Symbol): boolean { return !some(prop.declarations, isPrivateIdentifierPropertyDeclaration) && (!(prop.flags & (SymbolFlags.Method | SymbolFlags.GetAccessor | SymbolFlags.SetAccessor)) || - !prop.declarations.some(decl => isClassLike(decl.parent))); + !prop.declarations?.some(decl => isClassLike(decl.parent))); } function getSpreadSymbol(prop: Symbol, readonly: boolean) { @@ -15569,7 +15597,7 @@ namespace ts { } function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]) { - const declaration = type.objectFlags & ObjectFlags.Reference ? (type).node! : type.symbol.declarations[0]; + const declaration = type.objectFlags & ObjectFlags.Reference ? (type).node! : type.symbol.declarations![0]; const links = getNodeLinks(declaration); const target = type.objectFlags & ObjectFlags.Reference ? links.resolvedType! : type.objectFlags & ObjectFlags.Instantiated ? type.target! : type; @@ -16204,7 +16232,7 @@ namespace ts { if (resultObj.errors) { if (target.symbol && length(target.symbol.declarations)) { addRelatedInfo(resultObj.errors[resultObj.errors.length - 1], createDiagnosticForNode( - target.symbol.declarations[0], + target.symbol.declarations![0], Diagnostics.The_expected_type_comes_from_the_return_type_of_this_signature, )); } @@ -16301,7 +16329,7 @@ namespace ts { } if (!issuedElaboration && (targetProp && length(targetProp.declarations) || target.symbol && length(target.symbol.declarations))) { - const targetNode = targetProp && length(targetProp.declarations) ? targetProp.declarations[0] : target.symbol.declarations[0]; + const targetNode = targetProp && length(targetProp.declarations) ? targetProp.declarations![0] : target.symbol.declarations![0]; if (!getSourceFileOfNode(targetNode).hasNoDefaultLib) { addRelatedInfo(reportedDiag, createDiagnosticForNode( targetNode, @@ -17530,7 +17558,7 @@ namespace ts { } else { // use the property's value declaration if the property is assigned inside the literal itself - const objectLiteralDeclaration = source.symbol && firstOrUndefined(source.symbol.declarations); + const objectLiteralDeclaration = source.symbol?.declarations && firstOrUndefined(source.symbol.declarations); let suggestion: string | undefined; if (prop.valueDeclaration && findAncestor(prop.valueDeclaration, d => d === objectLiteralDeclaration) && getSourceFileOfNode(objectLiteralDeclaration) === getSourceFileOfNode(errorNode)) { const propDeclaration = prop.valueDeclaration as ObjectLiteralElementLike; @@ -18585,7 +18613,7 @@ namespace ts { const propName = symbolToString(unmatchedProperty); reportError(Diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2, propName, ...getTypeNamesForErrorDisplay(source, target)); if (length(unmatchedProperty.declarations)) { - associateRelatedInfo(createDiagnosticForNode(unmatchedProperty.declarations[0], Diagnostics._0_is_declared_here, propName)); + associateRelatedInfo(createDiagnosticForNode(unmatchedProperty.declarations![0], Diagnostics._0_is_declared_here, propName)); } if (shouldSkipElaboration && errorInfo) { overrideNextErrorInfo++; @@ -23431,7 +23459,7 @@ namespace ts { const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol); const sourceSymbol = localOrExportSymbol.flags & SymbolFlags.Alias ? resolveAlias(localOrExportSymbol) : localOrExportSymbol; - if (getDeclarationNodeFlagsFromSymbol(sourceSymbol) & NodeFlags.Deprecated && isUncalledFunctionReference(node, sourceSymbol)) { + if (sourceSymbol.declarations && getDeclarationNodeFlagsFromSymbol(sourceSymbol) & NodeFlags.Deprecated && isUncalledFunctionReference(node, sourceSymbol)) { addDeprecatedSuggestion(node, sourceSymbol.declarations, node.escapedText as string); } @@ -23504,7 +23532,7 @@ namespace ts { } } else if (isAlias) { - declaration = find(symbol.declarations, isSomeImportDeclaration); + declaration = symbol.declarations?.find(isSomeImportDeclaration); } else { return type; @@ -25941,7 +25969,7 @@ namespace ts { else if (propertiesOfJsxElementAttribPropInterface.length === 1) { return propertiesOfJsxElementAttribPropInterface[0].escapedName; } - else if (propertiesOfJsxElementAttribPropInterface.length > 1) { + else if (propertiesOfJsxElementAttribPropInterface.length > 1 && jsxElementAttribPropInterfaceSym!.declarations) { // More than one property on ElementAttributesProperty is an error error(jsxElementAttribPropInterfaceSym!.declarations[0], Diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, unescapeLeadingUnderscores(nameOfAttribPropContainer)); } @@ -26585,7 +26613,7 @@ namespace ts { } } else { - if (getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) { + if (prop.declarations && getDeclarationNodeFlagsFromSymbol(prop) & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) { addDeprecatedSuggestion(right, prop.declarations, right.escapedText as string); } checkPropertyNotUsedBeforeDeclaration(prop, node, right); @@ -29230,7 +29258,7 @@ namespace ts { if (allowSyntheticDefaultImports && type && type !== errorType) { const synthType = type as SyntheticDefaultModuleType; if (!synthType.syntheticType) { - const file = find(originalSymbol.declarations, isSourceFile); + const file = originalSymbol.declarations?.find(isSourceFile); const hasSyntheticDefault = canHaveSyntheticDefault(file, originalSymbol, /*dontResolveAlias*/ false); if (hasSyntheticDefault) { const memberTable = createSymbolTable(); @@ -31181,7 +31209,7 @@ namespace ts { if (propType.symbol && propType.symbol.flags & SymbolFlags.Class) { const name = prop.escapedName; const symbol = resolveName(prop.valueDeclaration, name, SymbolFlags.Type, undefined, name, /*isUse*/ false); - if (symbol && symbol.declarations.some(isJSDocTypedefTag)) { + if (symbol?.declarations && symbol.declarations.some(isJSDocTypedefTag)) { addDuplicateDeclarationErrorsForSymbols(symbol, Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(name), prop); addDuplicateDeclarationErrorsForSymbols(prop, Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(name), symbol); } @@ -32357,7 +32385,7 @@ namespace ts { const nodeSymbol = getSymbolOfNode(node as InterfaceDeclaration); // in case of merging interface declaration it is possible that we'll enter this check procedure several times for every declaration // to prevent this run check only for the first declaration of a given kind - if (nodeSymbol.declarations.length > 0 && nodeSymbol.declarations[0] !== node) { + if (nodeSymbol.declarations && nodeSymbol.declarations.length > 0 && nodeSymbol.declarations[0] !== node) { return; } } @@ -32366,7 +32394,7 @@ namespace ts { // 3.7.4: An object type can contain at most one string index signature and one numeric index signature. // 8.5: A class declaration can have at most one string index member declaration and one numeric index member declaration const indexSymbol = getIndexSymbol(getSymbolOfNode(node)!); - if (indexSymbol) { + if (indexSymbol?.declarations) { let seenNumericIndexer = false; let seenStringIndexer = false; for (const decl of indexSymbol.declarations) { @@ -32637,7 +32665,7 @@ namespace ts { if (some(symbol.declarations, d => isTypeDeclaration(d) && !!(d.flags & NodeFlags.Deprecated))) { addDeprecatedSuggestion( getDeprecatedSuggestionNode(node), - symbol.declarations, + symbol.declarations!, symbol.escapedName as string ); } @@ -32988,59 +33016,61 @@ namespace ts { let multipleConstructorImplementation = false; let hasNonAmbientClass = false; const functionDeclarations = [] as Declaration[]; - for (const current of declarations) { - const node = current; - const inAmbientContext = node.flags & NodeFlags.Ambient; - const inAmbientContextOrInterface = node.parent && (node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral) || inAmbientContext; - if (inAmbientContextOrInterface) { - // check if declarations are consecutive only if they are non-ambient - // 1. ambient declarations can be interleaved - // i.e. this is legal - // declare function foo(); - // declare function bar(); - // declare function foo(); - // 2. mixing ambient and non-ambient declarations is a separate error that will be reported - do not want to report an extra one - previousDeclaration = undefined; - } - - if ((node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && !inAmbientContext) { - hasNonAmbientClass = true; - } - - if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature || node.kind === SyntaxKind.Constructor) { - functionDeclarations.push(node); - const currentNodeFlags = getEffectiveDeclarationFlags(node, flagsToCheck); - someNodeFlags |= currentNodeFlags; - allNodeFlags &= currentNodeFlags; - someHaveQuestionToken = someHaveQuestionToken || hasQuestionToken(node); - allHaveQuestionToken = allHaveQuestionToken && hasQuestionToken(node); - const bodyIsPresent = nodeIsPresent((node as FunctionLikeDeclaration).body); - - if (bodyIsPresent && bodyDeclaration) { - if (isConstructor) { - multipleConstructorImplementation = true; + if (declarations) { + for (const current of declarations) { + const node = current; + const inAmbientContext = node.flags & NodeFlags.Ambient; + const inAmbientContextOrInterface = node.parent && (node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral) || inAmbientContext; + if (inAmbientContextOrInterface) { + // check if declarations are consecutive only if they are non-ambient + // 1. ambient declarations can be interleaved + // i.e. this is legal + // declare function foo(); + // declare function bar(); + // declare function foo(); + // 2. mixing ambient and non-ambient declarations is a separate error that will be reported - do not want to report an extra one + previousDeclaration = undefined; + } + + if ((node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression) && !inAmbientContext) { + hasNonAmbientClass = true; + } + + if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature || node.kind === SyntaxKind.Constructor) { + functionDeclarations.push(node); + const currentNodeFlags = getEffectiveDeclarationFlags(node, flagsToCheck); + someNodeFlags |= currentNodeFlags; + allNodeFlags &= currentNodeFlags; + someHaveQuestionToken = someHaveQuestionToken || hasQuestionToken(node); + allHaveQuestionToken = allHaveQuestionToken && hasQuestionToken(node); + const bodyIsPresent = nodeIsPresent((node as FunctionLikeDeclaration).body); + + if (bodyIsPresent && bodyDeclaration) { + if (isConstructor) { + multipleConstructorImplementation = true; + } + else { + duplicateFunctionDeclaration = true; + } } - else { - duplicateFunctionDeclaration = true; + else if (previousDeclaration?.parent === node.parent && previousDeclaration.end !== node.pos) { + reportImplementationExpectedError(previousDeclaration); } - } - else if (previousDeclaration?.parent === node.parent && previousDeclaration.end !== node.pos) { - reportImplementationExpectedError(previousDeclaration); - } - if (bodyIsPresent) { - if (!bodyDeclaration) { - bodyDeclaration = node as FunctionLikeDeclaration; + if (bodyIsPresent) { + if (!bodyDeclaration) { + bodyDeclaration = node as FunctionLikeDeclaration; + } + } + else { + hasOverloads = true; } - } - else { - hasOverloads = true; - } - previousDeclaration = node; + previousDeclaration = node; - if (!inAmbientContextOrInterface) { - lastSeenNonAmbientDeclaration = node as FunctionLikeDeclaration; + if (!inAmbientContextOrInterface) { + lastSeenNonAmbientDeclaration = node as FunctionLikeDeclaration; + } } } } @@ -33074,8 +33104,10 @@ namespace ts { } if (hasOverloads) { - checkFlagAgreementBetweenOverloads(declarations, bodyDeclaration, flagsToCheck, someNodeFlags, allNodeFlags); - checkQuestionTokenAgreementBetweenOverloads(declarations, bodyDeclaration, someHaveQuestionToken, allHaveQuestionToken); + if (declarations) { + checkFlagAgreementBetweenOverloads(declarations, bodyDeclaration, flagsToCheck, someNodeFlags, allNodeFlags); + checkQuestionTokenAgreementBetweenOverloads(declarations, bodyDeclaration, someHaveQuestionToken, allHaveQuestionToken); + } if (bodyDeclaration) { const signatures = getSignaturesOfSymbol(symbol); @@ -33118,7 +33150,7 @@ namespace ts { let exportedDeclarationSpaces = DeclarationSpaces.None; let nonExportedDeclarationSpaces = DeclarationSpaces.None; let defaultExportedDeclarationSpaces = DeclarationSpaces.None; - for (const d of symbol.declarations) { + for (const d of symbol.declarations!) { const declarationSpaces = getDeclarationSpaces(d); const effectiveDeclarationFlags = getEffectiveDeclarationFlags(d, ModifierFlags.Export | ModifierFlags.Default); @@ -33143,7 +33175,7 @@ namespace ts { if (commonDeclarationSpacesForExportsAndLocals || commonDeclarationSpacesForDefaultAndNonDefault) { // declaration spaces for exported and non-exported declarations intersect - for (const d of symbol.declarations) { + for (const d of symbol.declarations!) { const declarationSpaces = getDeclarationSpaces(d); const name = getNameOfDeclaration(d); @@ -33879,7 +33911,7 @@ namespace ts { // Since the javascript won't do semantic analysis like typescript, // if the javascript file comes before the typescript file and both contain same name functions, // checkFunctionOrConstructorSymbol wouldn't be called if we didnt ignore javascript function. - const firstDeclaration = find(localSymbol.declarations, + const firstDeclaration = localSymbol.declarations?.find( // Get first non javascript function declaration declaration => declaration.kind === node.kind && !(declaration.flags & NodeFlags.JavaScriptFile)); @@ -34045,7 +34077,8 @@ namespace ts { function checkUnusedTypeParameters(node: ClassLikeDeclaration | SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration, addDiagnostic: AddUnusedDiagnostic): void { // Only report errors on the last declaration for the type parameter container; // this ensures that all uses have been accounted for. - if (last(getSymbolOfNode(node).declarations) !== node) return; + const declarations = getSymbolOfNode(node).declarations; + if (!declarations || last(declarations) !== node) return; const typeParameters = getEffectiveTypeParameterDeclarations(node); const seenParentsWithEveryUnused = new Set(); @@ -34120,39 +34153,41 @@ namespace ts { return; } - for (const declaration of local.declarations) { - if (isValidUnusedLocalDeclaration(declaration)) { - continue; - } + if (local.declarations) { + for (const declaration of local.declarations) { + if (isValidUnusedLocalDeclaration(declaration)) { + continue; + } - if (isImportedDeclaration(declaration)) { - addToGroup(unusedImports, importClauseFromImported(declaration), declaration, getNodeId); - } - else if (isBindingElement(declaration) && isObjectBindingPattern(declaration.parent)) { - // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though. - const lastElement = last(declaration.parent.elements); - if (declaration === lastElement || !last(declaration.parent.elements).dotDotDotToken) { - addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId); + if (isImportedDeclaration(declaration)) { + addToGroup(unusedImports, importClauseFromImported(declaration), declaration, getNodeId); } - } - else if (isVariableDeclaration(declaration)) { - addToGroup(unusedVariables, declaration.parent, declaration, getNodeId); - } - else { - const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration); - const name = local.valueDeclaration && getNameOfDeclaration(local.valueDeclaration); - if (parameter && name) { - if (!isParameterPropertyDeclaration(parameter, parameter.parent) && !parameterIsThisKeyword(parameter) && !isIdentifierThatStartsWithUnderscore(name)) { - if (isBindingElement(declaration) && isArrayBindingPattern(declaration.parent)) { - addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId); - } - else { - addDiagnostic(parameter, UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local))); - } + else if (isBindingElement(declaration) && isObjectBindingPattern(declaration.parent)) { + // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though. + const lastElement = last(declaration.parent.elements); + if (declaration === lastElement || !last(declaration.parent.elements).dotDotDotToken) { + addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId); } } + else if (isVariableDeclaration(declaration)) { + addToGroup(unusedVariables, declaration.parent, declaration, getNodeId); + } else { - errorUnusedLocal(declaration, symbolName(local), addDiagnostic); + const parameter = local.valueDeclaration && tryGetRootParameterDeclaration(local.valueDeclaration); + const name = local.valueDeclaration && getNameOfDeclaration(local.valueDeclaration); + if (parameter && name) { + if (!isParameterPropertyDeclaration(parameter, parameter.parent) && !parameterIsThisKeyword(parameter) && !isIdentifierThatStartsWithUnderscore(name)) { + if (isBindingElement(declaration) && isArrayBindingPattern(declaration.parent)) { + addToGroup(unusedDestructures, declaration.parent, declaration, getNodeId); + } + else { + addDiagnostic(parameter, UnusedKind.Parameter, createDiagnosticForNode(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local))); + } + } + } + else { + errorUnusedLocal(declaration, symbolName(local), addDiagnostic); + } } } } @@ -34555,7 +34590,7 @@ namespace ts { checkTypeAssignableToAndOptionallyElaborate(checkExpressionCached(initializer), type, node, initializer, /*headMessage*/ undefined); } } - if (symbol.declarations.length > 1) { + if (symbol.declarations && symbol.declarations.length > 1) { if (some(symbol.declarations, d => d !== node && isVariableLike(d) && !areDeclarationFlagsIdentical(d, node))) { error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_modifiers, declarationNameToString(node.name)); } @@ -35927,7 +35962,7 @@ namespace ts { // condition 'errorNode === undefined' may appear if types does not declare nor string neither number indexer if (!errorNode && (getObjectFlags(type) & ObjectFlags.Interface)) { const someBaseTypeHasBothIndexers = forEach(getBaseTypes(type), base => getIndexTypeOfType(base, IndexKind.String) && getIndexTypeOfType(base, IndexKind.Number)); - errorNode = someBaseTypeHasBothIndexers ? undefined : type.symbol.declarations[0]; + errorNode = someBaseTypeHasBothIndexers || !type.symbol.declarations ? undefined : type.symbol.declarations[0]; } } @@ -35978,7 +36013,7 @@ namespace ts { // check if any base class already has both property and indexer. // check should be performed only if 'type' is the first type that brings property\indexer together const someBaseClassHasBothPropertyAndIndexer = forEach(getBaseTypes(containingType), base => getPropertyOfObjectType(base, prop.escapedName) && getIndexTypeOfType(base, indexKind)); - errorNode = someBaseClassHasBothPropertyAndIndexer ? undefined : containingType.symbol.declarations[0]; + errorNode = someBaseClassHasBothPropertyAndIndexer || !containingType.symbol.declarations ? undefined : containingType.symbol.declarations[0]; } if (errorNode && !isTypeAssignableTo(propertyType, indexType)) { @@ -36066,7 +36101,7 @@ namespace ts { /** Check that type parameter lists are identical across multiple declarations */ function checkTypeParameterListsIdentical(symbol: Symbol) { - if (symbol.declarations.length === 1) { + if (symbol.declarations && symbol.declarations.length === 1) { return; } @@ -36074,7 +36109,7 @@ namespace ts { if (!links.typeParametersChecked) { links.typeParametersChecked = true; const declarations = getClassOrInterfaceDeclarationsOfSymbol(symbol); - if (declarations.length <= 1) { + if (!declarations || declarations.length <= 1) { return; } @@ -36426,12 +36461,12 @@ namespace ts { error(getNameOfDeclaration(derived.valueDeclaration) || derived.valueDeclaration, errorMessage, symbolToString(base), typeToString(baseType), typeToString(type)); } else if (compilerOptions.useDefineForClassFields) { - const uninitialized = find(derived.declarations, d => d.kind === SyntaxKind.PropertyDeclaration && !(d as PropertyDeclaration).initializer); + const uninitialized = derived.declarations?.find(d => d.kind === SyntaxKind.PropertyDeclaration && !(d as PropertyDeclaration).initializer); if (uninitialized && !(derived.flags & SymbolFlags.Transient) && !(baseDeclarationFlags & ModifierFlags.Abstract) && !(derivedDeclarationFlags & ModifierFlags.Abstract) - && !derived.declarations.some(d => !!(d.flags & NodeFlags.Ambient))) { + && !derived.declarations?.some(d => !!(d.flags & NodeFlags.Ambient))) { const constructor = findConstructorDeclaration(getClassLikeDeclarationOfSymbol(type.symbol)!); const propName = (uninitialized as PropertyDeclaration).name; if ((uninitialized as PropertyDeclaration).exclamationToken @@ -36822,7 +36857,7 @@ namespace ts { const enumSymbol = getSymbolOfNode(node); const firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind); if (node === firstDeclaration) { - if (enumSymbol.declarations.length > 1) { + if (enumSymbol.declarations && enumSymbol.declarations.length > 1) { const enumIsConst = isEnumConst(node); // check that const is placed\omitted on all enum declarations forEach(enumSymbol.declarations, decl => { @@ -36865,11 +36900,13 @@ namespace ts { function getFirstNonAmbientClassOrFunctionDeclaration(symbol: Symbol): Declaration | undefined { const declarations = symbol.declarations; - for (const declaration of declarations) { - if ((declaration.kind === SyntaxKind.ClassDeclaration || - (declaration.kind === SyntaxKind.FunctionDeclaration && nodeIsPresent((declaration).body))) && - !(declaration.flags & NodeFlags.Ambient)) { - return declaration; + if (declarations) { + for (const declaration of declarations) { + if ((declaration.kind === SyntaxKind.ClassDeclaration || + (declaration.kind === SyntaxKind.FunctionDeclaration && nodeIsPresent((declaration).body))) && + !(declaration.flags & NodeFlags.Ambient)) { + return declaration; + } } } return undefined; @@ -36924,6 +36961,7 @@ namespace ts { // The following checks only apply on a non-ambient instantiated module declaration. if (symbol.flags & SymbolFlags.ValueModule && !inAmbientContext + && symbol.declarations && symbol.declarations.length > 1 && isInstantiatedModule(node, shouldPreserveConstEnums(compilerOptions))) { const firstNonAmbientClassOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol); @@ -37033,7 +37071,7 @@ namespace ts { let reportError = !(symbol.flags & SymbolFlags.Transient); if (!reportError) { // symbol should not originate in augmentation - reportError = !!symbol.parent && isExternalModuleAugmentation(symbol.parent.declarations[0]); + reportError = !!symbol.parent?.declarations && isExternalModuleAugmentation(symbol.parent.declarations[0]); } } break; @@ -37124,7 +37162,7 @@ namespace ts { error(node, Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type); } - if (isImportSpecifier(node) && every(target.declarations, d => !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated))) { + if (isImportSpecifier(node) && target.declarations?.every(d => !!(getCombinedNodeFlags(d) & NodeFlags.Deprecated))) { addDeprecatedSuggestion(node.name, target.declarations, symbol.escapedName as string); } } @@ -37336,7 +37374,7 @@ namespace ts { // find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases) const symbol = resolveName(exportedName, exportedName.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true); - if (symbol && (symbol === undefinedSymbol || symbol === globalThisSymbol || isGlobalSourceFile(getDeclarationContainer(symbol.declarations[0])))) { + if (symbol && (symbol === undefinedSymbol || symbol === globalThisSymbol || symbol.declarations && isGlobalSourceFile(getDeclarationContainer(symbol.declarations[0])))) { error(exportedName, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, idText(exportedName)); } else { @@ -37455,7 +37493,7 @@ namespace ts { return; } if (exportedDeclarationsCount > 1) { - for (const declaration of declarations) { + for (const declaration of declarations!) { if (isNotOverload(declaration)) { diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Cannot_redeclare_exported_variable_0, unescapeLeadingUnderscores(id))); } @@ -39296,10 +39334,12 @@ namespace ts { for (const s of arrayFrom(exports.values())) { if (s.mergeId) { const merged = getMergedSymbol(s); - for (const d of merged.declarations) { - const declFile = getSourceFileOfNode(d); - if (declFile === importTarget) { - return true; + if (merged.declarations) { + for (const d of merged.declarations) { + const declFile = getSourceFileOfNode(d); + if (declFile === importTarget) { + return true; + } } } } @@ -39340,17 +39380,19 @@ namespace ts { } // check what declarations in the symbol can contribute to the target meaning let typeReferenceDirectives: string[] | undefined; - for (const decl of symbol.declarations) { - // check meaning of the local symbol to see if declaration needs to be analyzed further - if (decl.symbol && decl.symbol.flags & meaning!) { - const file = getSourceFileOfNode(decl); - const typeReferenceDirective = fileToDirective.get(file.path); - if (typeReferenceDirective) { - (typeReferenceDirectives || (typeReferenceDirectives = [])).push(typeReferenceDirective); - } - else { - // found at least one entry that does not originate from type reference directive - return undefined; + if (symbol.declarations) { + for (const decl of symbol.declarations) { + // check meaning of the local symbol to see if declaration needs to be analyzed further + if (decl.symbol && decl.symbol.flags & meaning!) { + const file = getSourceFileOfNode(decl); + const typeReferenceDirective = fileToDirective.get(file.path); + if (typeReferenceDirective) { + (typeReferenceDirectives || (typeReferenceDirectives = [])).push(typeReferenceDirective); + } + else { + // found at least one entry that does not originate from type reference directive + return undefined; + } } } } @@ -39430,7 +39472,7 @@ namespace ts { // It is an error for a non-external-module (i.e. script) to declare its own `globalThis`. // We can't use `builtinGlobals` for this due to synthetic expando-namespace generation in JS files. const fileGlobalThisSymbol = file.locals!.get("globalThis" as __String); - if (fileGlobalThisSymbol) { + if (fileGlobalThisSymbol?.declarations) { for (const declaration of fileGlobalThisSymbol.declarations) { diagnostics.add(createDiagnosticForNode(declaration, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, "globalThis")); } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index e66ea4c2597bf..dafda8263ad89 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -292,7 +292,7 @@ namespace ts { return -1; } - export function countWhere(array: readonly T[], predicate: (x: T, i: number) => boolean): number { + export function countWhere(array: readonly T[] | undefined, predicate: (x: T, i: number) => boolean): number { let count = 0; if (array) { for (let i = 0; i < array.length; i++) { diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index e27f63eb29cda..c61980963278b 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -382,7 +382,7 @@ namespace ts.moduleSpecifiers { } function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol, checker: TypeChecker): string | undefined { - const decl = find(moduleSymbol.declarations, + const decl = moduleSymbol.declarations?.find( d => isNonGlobalAmbientModule(d) && (!isExternalModuleAugmentation(d) || !isExternalModuleNameRelative(getTextOfIdentifierOrLiteral(d.name))) ) as (ModuleDeclaration & { name: StringLiteral }) | undefined; if (decl) { diff --git a/src/compiler/tracing.ts b/src/compiler/tracing.ts index 2894e2a8bf72c..41e93d5c6a8e3 100644 --- a/src/compiler/tracing.ts +++ b/src/compiler/tracing.ts @@ -253,8 +253,8 @@ namespace ts { // eslint-disable-line one-namespace-per-file ...referenceProperties, ...conditionalProperties, firstDeclaration: firstDeclaration && { - path: firstFile.path, - start: indexFromOne(getLineAndCharacterOfPosition(firstFile, firstDeclaration.pos)), + path: firstFile!.path, + start: indexFromOne(getLineAndCharacterOfPosition(firstFile!, firstDeclaration.pos)), end: indexFromOne(getLineAndCharacterOfPosition(getSourceFileOfNode(firstDeclaration), firstDeclaration.end)), }, flags: Debug.formatTypeFlags(type.flags).split("|"), diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 69add2945f4ba..e20d1ac0bfca2 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -206,13 +206,15 @@ namespace ts { } function reportNonlocalAugmentation(containingFile: SourceFile, parentSymbol: Symbol, symbol: Symbol) { - const primaryDeclaration = find(parentSymbol.declarations, d => getSourceFileOfNode(d) === containingFile)!; + const primaryDeclaration = parentSymbol.declarations?.find(d => getSourceFileOfNode(d) === containingFile)!; const augmentingDeclarations = filter(symbol.declarations, d => getSourceFileOfNode(d) !== containingFile); - for (const augmentations of augmentingDeclarations) { - context.addDiagnostic(addRelatedInfo( - createDiagnosticForNode(augmentations, Diagnostics.Declaration_augments_declaration_in_another_file_This_cannot_be_serialized), - createDiagnosticForNode(primaryDeclaration, Diagnostics.This_is_the_declaration_being_augmented_Consider_moving_the_augmenting_declaration_into_the_same_file) - )); + if (augmentingDeclarations) { + for (const augmentations of augmentingDeclarations) { + context.addDiagnostic(addRelatedInfo( + createDiagnosticForNode(augmentations, Diagnostics.Declaration_augments_declaration_in_another_file_This_cannot_be_serialized), + createDiagnosticForNode(primaryDeclaration, Diagnostics.This_is_the_declaration_being_augmented_Consider_moving_the_augmenting_declaration_into_the_same_file) + )); + } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1e43dc112be2e..b052aa695fbeb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4714,7 +4714,7 @@ namespace ts { export interface Symbol { flags: SymbolFlags; // Symbol flags escapedName: __String; // Name of symbol - declarations: Declaration[]; // Declarations associated with this symbol + declarations?: Declaration[]; // Declarations associated with this symbol valueDeclaration: Declaration; // First value declaration of the symbol members?: SymbolTable; // Class, interface or object literal instance members exports?: SymbolTable; // Module exports diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3fb9d039bf0e1..5a260467b5f66 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -767,7 +767,7 @@ namespace ts { } export function getNonAugmentationDeclaration(symbol: Symbol) { - return find(symbol.declarations, d => !isExternalModuleAugmentation(d) && !(isModuleDeclaration(d) && isGlobalScopeAugmentation(d))); + return symbol.declarations?.find(d => !isExternalModuleAugmentation(d) && !(isModuleDeclaration(d) && isGlobalScopeAugmentation(d))); } export function isEffectiveExternalModule(node: SourceFile, compilerOptions: CompilerOptions) { @@ -4887,7 +4887,7 @@ namespace ts { } export function getLocalSymbolForExportDefault(symbol: Symbol) { - if (!isExportDefaultSymbol(symbol)) return undefined; + if (!isExportDefaultSymbol(symbol) || !symbol.declarations) return undefined; for (const decl of symbol.declarations) { if (decl.localSymbol) return decl.localSymbol; } @@ -4895,7 +4895,7 @@ namespace ts { } function isExportDefaultSymbol(symbol: Symbol): boolean { - return symbol && length(symbol.declarations) > 0 && hasSyntacticModifier(symbol.declarations[0], ModifierFlags.Default); + return symbol && length(symbol.declarations) > 0 && hasSyntacticModifier(symbol.declarations![0], ModifierFlags.Default); } /** Return ".ts", ".d.ts", or ".tsx", if that is the extension. */ @@ -5444,7 +5444,7 @@ namespace ts { } export function getClassLikeDeclarationOfSymbol(symbol: Symbol): ClassLikeDeclaration | undefined { - return find(symbol.declarations, isClassLike); + return symbol.declarations?.find(isClassLike); } export function getObjectFlags(type: Type): ObjectFlags { diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 454240b7a726d..ee0e8a37136c3 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -1003,8 +1003,8 @@ namespace FourSlash { private verifySymbol(symbol: ts.Symbol, declarationRanges: Range[]) { const { declarations } = symbol; - if (declarations.length !== declarationRanges.length) { - this.raiseError(`Expected to get ${declarationRanges.length} declarations, got ${declarations.length}`); + if (declarations?.length !== declarationRanges.length) { + this.raiseError(`Expected to get ${declarationRanges.length} declarations, got ${declarations?.length}`); } ts.zipWith(declarations, declarationRanges, (decl, range) => { diff --git a/src/services/callHierarchy.ts b/src/services/callHierarchy.ts index ca5a193e78f9c..2c2a83a22cba2 100644 --- a/src/services/callHierarchy.ts +++ b/src/services/callHierarchy.ts @@ -181,7 +181,7 @@ namespace ts.CallHierarchy { const indices = indicesOf(symbol.declarations); const keys = map(symbol.declarations, decl => ({ file: decl.getSourceFile().fileName, pos: decl.pos })); indices.sort((a, b) => compareStringsCaseSensitive(keys[a].file, keys[b].file) || keys[a].pos - keys[b].pos); - const sortedDeclarations = map(indices, i => symbol.declarations[i]); + const sortedDeclarations = map(indices, i => symbol.declarations![i]); let lastDecl: CallHierarchyDeclaration | undefined; for (const decl of sortedDeclarations) { if (isValidCallHierarchyDeclaration(decl)) { diff --git a/src/services/codefixes/convertFunctionToEs6Class.ts b/src/services/codefixes/convertFunctionToEs6Class.ts index 986eb07c53c07..500e4346cad6a 100644 --- a/src/services/codefixes/convertFunctionToEs6Class.ts +++ b/src/services/codefixes/convertFunctionToEs6Class.ts @@ -61,7 +61,7 @@ namespace ts.codefix { // all static members are stored in the "exports" array of symbol if (symbol.exports) { symbol.exports.forEach(member => { - if (member.name === "prototype") { + if (member.name === "prototype" && member.declarations) { const firstDeclaration = member.declarations[0]; // only one "x.prototype = { ... }" will pass if (member.declarations.length === 1 && diff --git a/src/services/codefixes/generateAccessors.ts b/src/services/codefixes/generateAccessors.ts index 99bd53e29343f..fbcfd1b6943aa 100644 --- a/src/services/codefixes/generateAccessors.ts +++ b/src/services/codefixes/generateAccessors.ts @@ -262,7 +262,7 @@ namespace ts.codefix { const superSymbol = superElement && checker.getSymbolAtLocation(superElement.expression); if (!superSymbol) break; const symbol = superSymbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(superSymbol) : superSymbol; - const superDecl = find(symbol.declarations, isClassLike); + const superDecl = symbol.declarations && find(symbol.declarations, isClassLike); if (!superDecl) break; res.push(superDecl); decl = superDecl; diff --git a/src/services/completions.ts b/src/services/completions.ts index f5f7176237b9b..9a1178ec0a174 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -877,7 +877,7 @@ namespace ts.Completions { } function isModuleSymbol(symbol: Symbol): boolean { - return symbol.declarations.some(d => d.kind === SyntaxKind.SourceFile); + return !!symbol.declarations?.some(d => d.kind === SyntaxKind.SourceFile); } function getCompletionData( @@ -1227,7 +1227,7 @@ namespace ts.Completions { const isValidAccess: (symbol: Symbol) => boolean = isNamespaceName // At `namespace N.M/**/`, if this is the only declaration of `M`, don't include `M` as a completion. - ? symbol => !!(symbol.flags & SymbolFlags.Namespace) && !symbol.declarations.every(d => d.parent === node.parent) + ? symbol => !!(symbol.flags & SymbolFlags.Namespace) && !symbol.declarations?.every(d => d.parent === node.parent) : isRhsOfImportDeclaration ? // Any kind is allowed when dotting off namespace in internal import equals declaration symbol => isValidTypeAccess(symbol) || isValidValueAccess(symbol) : diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index ab4ff4b2d4735..c4f6c8dea756a 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -839,7 +839,7 @@ namespace ts.FindAllReferences { } const exported = symbol.exports!.get(InternalSymbolName.ExportEquals); - if (exported) { + if (exported?.declarations) { for (const decl of exported.declarations) { const sourceFile = decl.getSourceFile(); if (sourceFilesSet.has(sourceFile.fileName)) { @@ -916,7 +916,7 @@ namespace ts.FindAllReferences { const result: SymbolAndEntries[] = []; const state = new State(sourceFiles, sourceFilesSet, node ? getSpecialSearchKind(node) : SpecialSearchKind.None, checker, cancellationToken, searchMeaning, options, result); - const exportSpecifier = !isForRenameWithPrefixAndSuffixText(options) ? undefined : find(symbol.declarations, isExportSpecifier); + const exportSpecifier = !isForRenameWithPrefixAndSuffixText(options) || !symbol.declarations ? undefined : find(symbol.declarations, isExportSpecifier); if (exportSpecifier) { // When renaming at an export specifier, rename the export and not the thing being exported. getReferencesAtExportSpecifier(exportSpecifier.name, symbol, exportSpecifier, state.createSearch(node, originalSymbol, /*comingFrom*/ undefined), state, /*addReferencesHere*/ true, /*alwaysGetReferences*/ true); diff --git a/src/services/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts index d2b17739824a0..563e10680522a 100644 --- a/src/services/getEditsForFileRename.ts +++ b/src/services/getEditsForFileRename.ts @@ -146,7 +146,7 @@ namespace ts { importLiteral => { const importedModuleSymbol = program.getTypeChecker().getSymbolAtLocation(importLiteral); // No need to update if it's an ambient module^M - if (importedModuleSymbol && importedModuleSymbol.declarations.some(d => isAmbientModule(d))) return undefined; + if (importedModuleSymbol?.declarations && importedModuleSymbol.declarations.some(d => isAmbientModule(d))) return undefined; const toImport = oldFromNew !== undefined // If we're at the new location (file was already renamed), need to redo module resolution starting from the old location. @@ -185,7 +185,7 @@ namespace ts { ): ToImport | undefined { if (importedModuleSymbol) { // `find` should succeed because we checked for ambient modules before calling this function. - const oldFileName = find(importedModuleSymbol.declarations, isSourceFile)!.fileName; + const oldFileName = find(importedModuleSymbol.declarations!, isSourceFile)!.fileName; const newFileName = oldToNew(oldFileName); return newFileName === undefined ? { newFileName: oldFileName, updated: false } : { newFileName, updated: true }; } diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index e1ea158f138f3..532fbbe9ba120 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -51,7 +51,7 @@ namespace ts.GoToDefinition { // assignment. This case and others are handled by the following code. if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); - const definitions = shorthandSymbol ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node)) : emptyArray; + const definitions = shorthandSymbol?.declarations ? shorthandSymbol.declarations.map(decl => createDefinitionInfo(decl, typeChecker, shorthandSymbol, node)) : emptyArray; return concatenate(definitions, getDefinitionFromObjectLiteralElement(typeChecker, node) || emptyArray); } @@ -206,7 +206,7 @@ namespace ts.GoToDefinition { // get the aliased symbol instead. This allows for goto def on an import e.g. // import {A, B} from "mod"; // to jump to the implementation directly. - if (symbol && symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) { + if (symbol?.declarations && symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) { const aliased = checker.getAliasedSymbol(symbol); if (aliased.declarations) { return aliased; @@ -254,7 +254,7 @@ namespace ts.GoToDefinition { // Applicable only if we are in a new expression, or we are on a constructor declaration // and in either case the symbol has a construct signature definition, i.e. class if (symbol.flags & SymbolFlags.Class && !(symbol.flags & (SymbolFlags.Function | SymbolFlags.Variable)) && (isNewExpressionTarget(node) || node.kind === SyntaxKind.ConstructorKeyword)) { - const cls = find(filteredDeclarations, isClassLike) || Debug.fail("Expected declaration to have at least one class-like declaration"); + const cls = find(filteredDeclarations!, isClassLike) || Debug.fail("Expected declaration to have at least one class-like declaration"); return getSignatureDefinition(cls.members, /*selectConstructors*/ true); } } diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index d0256252ebcc5..d86e497330b28 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -62,9 +62,11 @@ namespace ts.FindAllReferences { } // Module augmentations may use this module's exports without importing it. - for (const decl of exportingModuleSymbol.declarations) { - if (isExternalModuleAugmentation(decl) && sourceFilesSet.has(decl.getSourceFile().fileName)) { - addIndirectUser(decl); + if (exportingModuleSymbol.declarations) { + for (const decl of exportingModuleSymbol.declarations) { + if (isExternalModuleAugmentation(decl) && sourceFilesSet.has(decl.getSourceFile().fileName)) { + addIndirectUser(decl); + } } } @@ -468,7 +470,7 @@ namespace ts.FindAllReferences { if (parent.kind === SyntaxKind.PropertyAccessExpression) { // When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use. // So check that we are at the declaration. - return symbol.declarations.some(d => d === parent) && isBinaryExpression(grandParent) + return symbol.declarations?.some(d => d === parent) && isBinaryExpression(grandParent) ? getSpecialPropertyExport(grandParent, /*useLhsSymbol*/ false) : undefined; } diff --git a/src/services/refactors/convertOverloadListToSingleSignature.ts b/src/services/refactors/convertOverloadListToSingleSignature.ts index cd0e4d9cd8ef4..5c49bf06d0970 100644 --- a/src/services/refactors/convertOverloadListToSingleSignature.ts +++ b/src/services/refactors/convertOverloadListToSingleSignature.ts @@ -201,10 +201,10 @@ ${newComment.split("\n").map(c => ` * ${c}`).join("\n")} if (!every(decls, d => getSourceFileOfNode(d) === file)) { return; } - if (!isConvertableSignatureDeclaration(decls[0])) { + if (!isConvertableSignatureDeclaration(decls![0])) { return; } - const kindOne = decls[0].kind; + const kindOne = decls![0].kind; if (!every(decls, d => d.kind === kindOne)) { return; } diff --git a/src/services/refactors/convertParamsToDestructuredObject.ts b/src/services/refactors/convertParamsToDestructuredObject.ts index c9e587837ddd9..28bde59adaef6 100644 --- a/src/services/refactors/convertParamsToDestructuredObject.ts +++ b/src/services/refactors/convertParamsToDestructuredObject.ts @@ -360,7 +360,7 @@ namespace ts.refactor.convertParamsToDestructuredObject { if (isObjectLiteralExpression(functionDeclaration.parent)) { const contextualSymbol = getSymbolForContextualType(functionDeclaration.name, checker); // don't offer the refactor when there are multiple signatures since we won't know which ones the user wants to change - return contextualSymbol?.declarations.length === 1 && isSingleImplementation(functionDeclaration, checker); + return contextualSymbol?.declarations?.length === 1 && isSingleImplementation(functionDeclaration, checker); } return isSingleImplementation(functionDeclaration, checker); case SyntaxKind.Constructor: diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts index ab85bb198cc62..e9cecdf6c9378 100644 --- a/src/services/refactors/extractType.ts +++ b/src/services/refactors/extractType.ts @@ -145,7 +145,7 @@ namespace ts.refactor { if (isTypeReferenceNode(node)) { if (isIdentifier(node.typeName)) { const symbol = checker.resolveName(node.typeName.text, node.typeName, SymbolFlags.TypeParameter, /* excludeGlobals */ true); - if (symbol) { + if (symbol?.declarations) { const declaration = cast(first(symbol.declarations), isTypeParameterDeclaration); if (rangeContainsSkipTrivia(statement, declaration, file) && !rangeContainsSkipTrivia(selection, declaration, file)) { pushIfUnique(result, declaration); diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index be93b513244e1..c371ac6301d06 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -443,20 +443,22 @@ namespace ts.refactor { const oldFileNamedImports: string[] = []; const markSeenTop = nodeSeenTracker(); // Needed because multiple declarations may appear in `const x = 0, y = 1;`. newFileImportsFromOldFile.forEach(symbol => { - for (const decl of symbol.declarations) { - if (!isTopLevelDeclaration(decl)) continue; - const name = nameOfTopLevelDeclaration(decl); - if (!name) continue; - - const top = getTopLevelDeclarationStatement(decl); - if (markSeenTop(top)) { - addExportToChanges(oldFile, top, changes, useEs6ModuleSyntax); - } - if (hasSyntacticModifier(decl, ModifierFlags.Default)) { - oldFileDefault = name; - } - else { - oldFileNamedImports.push(name.text); + if (symbol.declarations) { + for (const decl of symbol.declarations) { + if (!isTopLevelDeclaration(decl)) continue; + const name = nameOfTopLevelDeclaration(decl); + if (!name) continue; + + const top = getTopLevelDeclarationStatement(decl); + if (markSeenTop(top)) { + addExportToChanges(oldFile, top, changes, useEs6ModuleSyntax); + } + if (hasSyntacticModifier(decl, ModifierFlags.Default)) { + oldFileDefault = name; + } + else { + oldFileNamedImports.push(name.text); + } } } }); diff --git a/src/services/rename.ts b/src/services/rename.ts index e75de5151cc5a..98e422cf01ea2 100644 --- a/src/services/rename.ts +++ b/src/services/rename.ts @@ -60,7 +60,7 @@ namespace ts.Rename { return getRenameInfoError(Diagnostics.You_cannot_rename_a_module_via_a_global_import); } - const moduleSourceFile = find(moduleSymbol.declarations, isSourceFile); + const moduleSourceFile = moduleSymbol.declarations && find(moduleSymbol.declarations, isSourceFile); if (!moduleSourceFile) return undefined; const withoutIndex = endsWith(node.text, "/index") || endsWith(node.text, "/index.js") ? undefined : tryRemoveSuffix(removeFileExtension(moduleSourceFile.fileName), "/index"); const name = withoutIndex === undefined ? moduleSourceFile.fileName : withoutIndex; diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 19bfaba4499ef..589d4d4b8bf7b 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -401,8 +401,8 @@ namespace ts.SymbolDisplay { if (symbolFlags & SymbolFlags.EnumMember) { symbolKind = ScriptElementKind.enumMemberElement; addPrefixForAnyFunctionOrVar(symbol, "enum member"); - const declaration = symbol.declarations[0]; - if (declaration.kind === SyntaxKind.EnumMember) { + const declaration = symbol.declarations?.[0]; + if (declaration?.kind === SyntaxKind.EnumMember) { const constantValue = typeChecker.getConstantValue(declaration); if (constantValue !== undefined) { displayParts.push(spacePart()); @@ -446,22 +446,24 @@ namespace ts.SymbolDisplay { } } - switch (symbol.declarations[0].kind) { - case SyntaxKind.NamespaceExportDeclaration: - displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); - displayParts.push(spacePart()); - displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword)); - break; - case SyntaxKind.ExportAssignment: - displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); - displayParts.push(spacePart()); - displayParts.push(keywordPart((symbol.declarations[0] as ExportAssignment).isExportEquals ? SyntaxKind.EqualsToken : SyntaxKind.DefaultKeyword)); - break; - case SyntaxKind.ExportSpecifier: - displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); - break; - default: - displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); + if (symbol.declarations) { + switch (symbol.declarations[0].kind) { + case SyntaxKind.NamespaceExportDeclaration: + displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); + displayParts.push(spacePart()); + displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword)); + break; + case SyntaxKind.ExportAssignment: + displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); + displayParts.push(spacePart()); + displayParts.push(keywordPart((symbol.declarations[0] as ExportAssignment).isExportEquals ? SyntaxKind.EqualsToken : SyntaxKind.DefaultKeyword)); + break; + case SyntaxKind.ExportSpecifier: + displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); + break; + default: + displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); + } } displayParts.push(spacePart()); addFullSymbolName(symbol); @@ -556,7 +558,7 @@ namespace ts.SymbolDisplay { // For some special property access expressions like `exports.foo = foo` or `module.exports.foo = foo` // there documentation comments might be attached to the right hand side symbol of their declarations. // The pattern of such special property access is that the parent symbol is the symbol of the file. - if (symbol.parent && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) { + if (symbol.parent && symbol.declarations && forEach(symbol.parent.declarations, declaration => declaration.kind === SyntaxKind.SourceFile)) { for (const declaration of symbol.declarations) { if (!declaration.parent || declaration.parent.kind !== SyntaxKind.BinaryExpression) { continue; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 32e43fc0c4c77..eaf056272d4d8 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2407,7 +2407,7 @@ declare namespace ts { export interface Symbol { flags: SymbolFlags; escapedName: __String; - declarations: Declaration[]; + declarations?: Declaration[]; valueDeclaration: Declaration; members?: SymbolTable; exports?: SymbolTable; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 87bb3c3e61ef8..7573c812fd7d2 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2407,7 +2407,7 @@ declare namespace ts { export interface Symbol { flags: SymbolFlags; escapedName: __String; - declarations: Declaration[]; + declarations?: Declaration[]; valueDeclaration: Declaration; members?: SymbolTable; exports?: SymbolTable; From 3b677d150e930298ae7902f5adaadf8a58fe1ce7 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 25 Feb 2021 17:14:59 -0800 Subject: [PATCH 2/3] undo clever change to getDeclaringConstructor --- src/compiler/checker.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3019ed19f2d80..3729b6333965a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8360,12 +8360,14 @@ namespace ts { } function getDeclaringConstructor(symbol: Symbol) { - return symbol.declarations?.find(declaration => { - const container = getThisContainer(declaration, /*includeArrowFunctions*/ false); - if (container && (container.kind === SyntaxKind.Constructor || isJSConstructor(container))) { - return container; - } - }) as ConstructorDeclaration | undefined; + if (symbol.declarations) { + for (const declaration of symbol.declarations) { + const container = getThisContainer(declaration, /*includeArrowFunctions*/ false); + if (container && (container.kind === SyntaxKind.Constructor || isJSConstructor(container))) { + return container as ConstructorDeclaration; + } + }; + } } function getFlowTypeInConstructor(symbol: Symbol, constructor: ConstructorDeclaration) { From 7dc23a39fcd69f5cff38a81948768181ba723a46 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 1 Mar 2021 13:47:34 -0800 Subject: [PATCH 3/3] Address PR comments 1. More early-returns. 2. More line breaks. --- src/compiler/builderState.ts | 17 ++-- src/compiler/checker.ts | 129 ++++++++++++------------ src/services/refactors/moveToNewFile.ts | 33 +++--- 3 files changed, 90 insertions(+), 89 deletions(-) diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 2ef640d540616..a2c7741f0e8b0 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -174,14 +174,15 @@ namespace ts { return referencedFiles; function addReferenceFromAmbientModule(symbol: Symbol) { - if (symbol.declarations) { - // Add any file other than our own as reference - for (const declaration of symbol.declarations) { - const declarationSourceFile = getSourceFileOfNode(declaration); - if (declarationSourceFile && - declarationSourceFile !== sourceFile) { - addReferencedFile(declarationSourceFile.resolvedPath); - } + if (!symbol.declarations) { + return; + } + // Add any file other than our own as reference + for (const declaration of symbol.declarations) { + const declarationSourceFile = getSourceFileOfNode(declaration); + if (declarationSourceFile && + declarationSourceFile !== sourceFile) { + addReferencedFile(declarationSourceFile.resolvedPath); } } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fb6dc90c03704..9d8edbe12a5fa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1243,7 +1243,10 @@ namespace ts { // as we will already report a "Declaration name conflicts..." error, and this error // won't make much sense. if (target !== globalThisSymbol) { - error(source.declarations && getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target)); + error( + source.declarations && getNameOfDeclaration(source.declarations[0]), + Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, + symbolToString(target)); } } else { // error @@ -8362,14 +8365,15 @@ namespace ts { } function getDeclaringConstructor(symbol: Symbol) { - if (symbol.declarations) { - for (const declaration of symbol.declarations) { - const container = getThisContainer(declaration, /*includeArrowFunctions*/ false); - if (container && (container.kind === SyntaxKind.Constructor || isJSConstructor(container))) { - return container as ConstructorDeclaration; - } - }; + if (!symbol.declarations) { + return; } + for (const declaration of symbol.declarations) { + const container = getThisContainer(declaration, /*includeArrowFunctions*/ false); + if (container && (container.kind === SyntaxKind.Constructor || isJSConstructor(container))) { + return container as ConstructorDeclaration; + } + }; } function getFlowTypeInConstructor(symbol: Symbol, constructor: ConstructorDeclaration) { @@ -8413,9 +8417,9 @@ namespace ts { type = getFlowTypeInConstructor(symbol, getDeclaringConstructor(symbol)!); } if (!type) { - let jsdocType: Type | undefined; let types: Type[] | undefined; if (symbol.declarations) { + let jsdocType: Type | undefined; for (const declaration of symbol.declarations) { const expression = (isBinaryExpression(declaration) || isCallExpression(declaration)) ? declaration : isAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration : @@ -8442,8 +8446,8 @@ namespace ts { (types || (types = [])).push((isBinaryExpression(expression) || isCallExpression(expression)) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType); } } + type = jsdocType; } - type = jsdocType; if (!type) { if (!length(types)) { return errorType; // No types from any declarations :( @@ -9286,17 +9290,18 @@ namespace ts { // The local type parameters are the combined set of type parameters from all declarations of the class, // interface, or type alias. function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] | undefined { + if (!symbol.declarations) { + return; + } let result: TypeParameter[] | undefined; - if (symbol.declarations) { - for (const node of symbol.declarations) { - if (node.kind === SyntaxKind.InterfaceDeclaration || - node.kind === SyntaxKind.ClassDeclaration || - node.kind === SyntaxKind.ClassExpression || - isJSConstructor(node) || - isTypeAlias(node)) { - const declaration = node; - result = appendTypeParameters(result, getEffectiveTypeParameterDeclarations(declaration)); - } + for (const node of symbol.declarations) { + if (node.kind === SyntaxKind.InterfaceDeclaration || + node.kind === SyntaxKind.ClassDeclaration || + node.kind === SyntaxKind.ClassExpression || + isJSConstructor(node) || + isTypeAlias(node)) { + const declaration = node; + result = appendTypeParameters(result, getEffectiveTypeParameterDeclarations(declaration)); } } return result; @@ -9587,20 +9592,21 @@ namespace ts { * and if none of the base interfaces have a "this" type. */ function isThislessInterface(symbol: Symbol): boolean { - if (symbol.declarations) { - for (const declaration of symbol.declarations) { - if (declaration.kind === SyntaxKind.InterfaceDeclaration) { - if (declaration.flags & NodeFlags.ContainsThis) { - return false; - } - const baseTypeNodes = getInterfaceBaseTypeNodes(declaration); - if (baseTypeNodes) { - for (const node of baseTypeNodes) { - if (isEntityNameExpression(node.expression)) { - const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); - if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { - return false; - } + if (!symbol.declarations) { + return true; + } + for (const declaration of symbol.declarations) { + if (declaration.kind === SyntaxKind.InterfaceDeclaration) { + if (declaration.flags & NodeFlags.ContainsThis) { + return false; + } + const baseTypeNodes = getInterfaceBaseTypeNodes(declaration); + if (baseTypeNodes) { + for (const node of baseTypeNodes) { + if (isEntityNameExpression(node.expression)) { + const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); + if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { + return false; } } } @@ -12048,23 +12054,21 @@ namespace ts { } function getSignaturesOfSymbol(symbol: Symbol | undefined): Signature[] { - if (!symbol) return emptyArray; + if (!symbol || !symbol.declarations) return emptyArray; const result: Signature[] = []; - if (symbol.declarations) { - for (let i = 0; i < symbol.declarations.length; i++) { - const decl = symbol.declarations[i]; - if (!isFunctionLike(decl)) continue; - // Don't include signature if node is the implementation of an overloaded function. A node is considered - // an implementation node if it has a body and the previous node is of the same kind and immediately - // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). - if (i > 0 && (decl as FunctionLikeDeclaration).body) { - const previous = symbol.declarations[i - 1]; - if (decl.parent === previous.parent && decl.kind === previous.kind && decl.pos === previous.end) { - continue; - } + for (let i = 0; i < symbol.declarations.length; i++) { + const decl = symbol.declarations[i]; + if (!isFunctionLike(decl)) continue; + // Don't include signature if node is the implementation of an overloaded function. A node is considered + // an implementation node if it has a body and the previous node is of the same kind and immediately + // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). + if (i > 0 && (decl as FunctionLikeDeclaration).body) { + const previous = symbol.declarations[i - 1]; + if (decl.parent === previous.parent && decl.kind === previous.kind && decl.pos === previous.end) { + continue; } - result.push(getSignatureFromDeclaration(decl)); } + result.push(getSignatureFromDeclaration(decl)); } return result; } @@ -39497,27 +39501,22 @@ namespace ts { // defined here to avoid outer scope pollution function getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[] | undefined { // program does not have any files with type reference directives - bail out - if (!fileToDirective) { - return undefined; - } - if (!isSymbolFromTypeDeclarationFile(symbol)) { + if (!fileToDirective || !isSymbolFromTypeDeclarationFile(symbol)) { return undefined; } // check what declarations in the symbol can contribute to the target meaning let typeReferenceDirectives: string[] | undefined; - if (symbol.declarations) { - for (const decl of symbol.declarations) { - // check meaning of the local symbol to see if declaration needs to be analyzed further - if (decl.symbol && decl.symbol.flags & meaning!) { - const file = getSourceFileOfNode(decl); - const typeReferenceDirective = fileToDirective.get(file.path); - if (typeReferenceDirective) { - (typeReferenceDirectives || (typeReferenceDirectives = [])).push(typeReferenceDirective); - } - else { - // found at least one entry that does not originate from type reference directive - return undefined; - } + for (const decl of symbol.declarations!) { + // check meaning of the local symbol to see if declaration needs to be analyzed further + if (decl.symbol && decl.symbol.flags & meaning!) { + const file = getSourceFileOfNode(decl); + const typeReferenceDirective = fileToDirective.get(file.path); + if (typeReferenceDirective) { + (typeReferenceDirectives || (typeReferenceDirectives = [])).push(typeReferenceDirective); + } + else { + // found at least one entry that does not originate from type reference directive + return undefined; } } } diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index c371ac6301d06..ad707f8d2e7ba 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -443,22 +443,23 @@ namespace ts.refactor { const oldFileNamedImports: string[] = []; const markSeenTop = nodeSeenTracker(); // Needed because multiple declarations may appear in `const x = 0, y = 1;`. newFileImportsFromOldFile.forEach(symbol => { - if (symbol.declarations) { - for (const decl of symbol.declarations) { - if (!isTopLevelDeclaration(decl)) continue; - const name = nameOfTopLevelDeclaration(decl); - if (!name) continue; - - const top = getTopLevelDeclarationStatement(decl); - if (markSeenTop(top)) { - addExportToChanges(oldFile, top, changes, useEs6ModuleSyntax); - } - if (hasSyntacticModifier(decl, ModifierFlags.Default)) { - oldFileDefault = name; - } - else { - oldFileNamedImports.push(name.text); - } + if (!symbol.declarations) { + return; + } + for (const decl of symbol.declarations) { + if (!isTopLevelDeclaration(decl)) continue; + const name = nameOfTopLevelDeclaration(decl); + if (!name) continue; + + const top = getTopLevelDeclarationStatement(decl); + if (markSeenTop(top)) { + addExportToChanges(oldFile, top, changes, useEs6ModuleSyntax); + } + if (hasSyntacticModifier(decl, ModifierFlags.Default)) { + oldFileDefault = name; + } + else { + oldFileNamedImports.push(name.text); } } });