diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 199cb60c3c7..ab4f961e3a0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2695,7 +2695,7 @@ namespace ts { writeSpace(writer); let type = getTypeOfSymbol(p); - if (strictNullChecks && parameterNode.initializer && !(getModifierFlags(parameterNode) & ModifierFlags.ParameterPropertyModifier)) { + if (isRequiredInitializedParameter(parameterNode)) { type = includeFalsyTypes(type, TypeFlags.Undefined); } buildTypeDisplay(type, writer, enclosingDeclaration, flags, symbolStack); @@ -3182,12 +3182,6 @@ namespace ts { } return parentType; } - // In strict null checking mode, a default value of a binding pattern adds undefined, - // which should be removed to get the type of the elements - const func = getContainingFunction(declaration); - if (strictNullChecks && func && !func.body && getFalsyFlags(parentType) & TypeFlags.Undefined) { - parentType = getTypeWithFacts(parentType, TypeFacts.NEUndefined); - } let type: Type; if (pattern.kind === SyntaxKind.ObjectBindingPattern) { @@ -20554,6 +20548,13 @@ namespace ts { return false; } + function isRequiredInitializedParameter(parameter: ParameterDeclaration) { + return strictNullChecks && + !isOptionalParameter(parameter) && + parameter.initializer && + !(getModifierFlags(parameter) & ModifierFlags.ParameterPropertyModifier); + } + function getNodeCheckFlags(node: Node): NodeCheckFlags { node = getParseTreeNode(node); return node ? getNodeLinks(node).flags : undefined; @@ -20648,13 +20649,9 @@ namespace ts { let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature)) ? getWidenedLiteralType(getTypeOfSymbol(symbol)) : unknownType; - if (strictNullChecks && - declaration.kind === SyntaxKind.Parameter && - (declaration as ParameterDeclaration).initializer && - !(getModifierFlags(declaration) & ModifierFlags.ParameterPropertyModifier)) { + if (flags & TypeFormatFlags.AddUndefined) { type = includeFalsyTypes(type, TypeFlags.Undefined); } - getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags); } @@ -20753,6 +20750,7 @@ namespace ts { isTopLevelValueImportEqualsWithEntityName, isDeclarationVisible, isImplementationOfOverload, + isRequiredInitializedParameter, writeTypeOfDeclaration, writeReturnTypeOfSignatureDeclaration, writeTypeOfExpression, diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 93c4e9578f2..95a56b12dd3 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -322,13 +322,22 @@ namespace ts { function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, type: TypeNode, getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) { writer.getSymbolAccessibilityDiagnostic = getSymbolAccessibilityDiagnostic; write(": "); - if (type) { + + // use the checker's type, not the declared type, + // for non-optional initialized parameters that aren't a parameter property + const shouldUseResolverType = declaration.kind === SyntaxKind.Parameter && + resolver.isRequiredInitializedParameter(declaration as ParameterDeclaration); + if (type && !shouldUseResolverType) { // Write the type emitType(type); } else { errorNameNode = declaration.name; - resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue, writer); + let format = TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue; + if (shouldUseResolverType) { + format |= TypeFormatFlags.AddUndefined; + } + resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, format, writer); errorNameNode = undefined; } } @@ -1594,12 +1603,7 @@ namespace ts { emitTypeOfVariableDeclarationFromTypeLiteral(node); } else if (!hasModifier(node.parent, ModifierFlags.Private)) { - // use the checker's type, not the declared type, - // for optional parameters and initialized ones that aren't a parameter property - const typeShouldAddUndefined = resolver.isOptionalParameter(node) || - node.initializer && !(getModifierFlags(node) & ModifierFlags.ParameterPropertyModifier); - const typeNode = typeShouldAddUndefined ? undefined : node.type; - writeTypeOfDeclaration(node, typeNode, getParameterDeclarationTypeVisibilityError); + writeTypeOfDeclaration(node, node.type, getParameterDeclarationTypeVisibilityError); } function getParameterDeclarationTypeVisibilityError(symbolAccessibilityResult: SymbolAccessibilityResult): SymbolAccessibilityDiagnostic { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8b49af402cf..48feb6bb092 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2466,7 +2466,8 @@ InFirstTypeArgument = 0x00000100, // Writing first type argument of the instantiated type InTypeAlias = 0x00000200, // Writing type in type alias declaration UseTypeAliasValue = 0x00000400, // Serialize the type instead of using type-alias. This is needed when we emit declaration file. - SuppressAnyReturnType = 0x00000800, // If the return type is any-like, don't offer a return type. + SuppressAnyReturnType = 0x00000800, // If the return type is any-like, don't offer a return type. + AddUndefined = 0x00001000, // Add undefined to types of initialized, non-optional parameters } export const enum SymbolFormatFlags { @@ -2571,6 +2572,7 @@ isDeclarationVisible(node: Declaration): boolean; collectLinkedAliases(node: Identifier): Node[]; isImplementationOfOverload(node: FunctionLikeDeclaration): boolean; + isRequiredInitializedParameter(node: ParameterDeclaration): boolean; writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;