diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8d144266b8b..27f9ed6f1b1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -48132,6 +48132,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } + function declaredParameterTypeContainsUndefined(parameter: ParameterDeclaration) { + if (!parameter.type) return false; + const type = getTypeFromTypeNode(parameter.type); + return containsUndefinedType(type); + } + function requiresAddingImplicitUndefined(parameter: ParameterDeclaration) { + return (isRequiredInitializedParameter(parameter) || isOptionalUninitializedParameterProperty(parameter)) && !declaredParameterTypeContainsUndefined(parameter); + } + function isRequiredInitializedParameter(parameter: ParameterDeclaration | JSDocParameterTag): boolean { return !!strictNullChecks && !isOptionalParameter(parameter) && @@ -48525,8 +48534,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { isTopLevelValueImportEqualsWithEntityName, isDeclarationVisible, isImplementationOfOverload, - isRequiredInitializedParameter, - isOptionalUninitializedParameterProperty, + requiresAddingImplicitUndefined, isExpandoFunctionDeclaration, getPropertiesOfContainerFunction, createTypeOfDeclaration, diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 54f407de035..1abd8cdf05c 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1091,8 +1091,7 @@ export const notImplementedResolver: EmitResolver = { isLateBound: (_node): _node is LateBoundDeclaration => false, collectLinkedAliases: notImplemented, isImplementationOfOverload: notImplemented, - isRequiredInitializedParameter: notImplemented, - isOptionalUninitializedParameterProperty: notImplemented, + requiresAddingImplicitUndefined: notImplemented, isExpandoFunctionDeclaration: notImplemented, getPropertiesOfContainerFunction: notImplemented, createTypeOfDeclaration: notImplemented, diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index bcbb544a5ce..e4163f67d87 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -127,7 +127,6 @@ import { isModuleDeclaration, isOmittedExpression, isPrivateIdentifier, - isPropertySignature, isSemicolonClassElement, isSetAccessorDeclaration, isSourceFile, @@ -708,7 +707,6 @@ export function transformDeclarations(context: TransformationContext) { | FunctionDeclaration | MethodDeclaration | GetAccessorDeclaration - | SetAccessorDeclaration | BindingElement | ConstructSignatureDeclaration | VariableDeclaration @@ -727,46 +725,43 @@ export function transformDeclarations(context: TransformationContext) { // Literal const declarations will have an initializer ensured rather than a type return; } - const shouldUseResolverType = node.kind === SyntaxKind.Parameter && - (resolver.isRequiredInitializedParameter(node) || - resolver.isOptionalUninitializedParameterProperty(node)); - if (type && !shouldUseResolverType) { + const shouldAddImplicitUndefined = node.kind === SyntaxKind.Parameter && resolver.requiresAddingImplicitUndefined(node); + if (type && !shouldAddImplicitUndefined) { return visitNode(type, visitDeclarationSubtree, isTypeNode); } - if (!getParseTreeNode(node)) { - return type ? visitNode(type, visitDeclarationSubtree, isTypeNode) : factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - } - if (node.kind === SyntaxKind.SetAccessor) { - // Set accessors with no associated type node (from it's param or get accessor return) are `any` since they are never contextually typed right now - // (The inferred type here will be void, but the old declaration emitter printed `any`, so this replicates that) - return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); - } + errorNameNode = node.name; let oldDiag: typeof getSymbolAccessibilityDiagnostic; if (!suppressNewDiagnosticContexts) { oldDiag = getSymbolAccessibilityDiagnostic; getSymbolAccessibilityDiagnostic = createGetSymbolAccessibilityDiagnosticForNode(node); } - if (node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement) { - return cleanup(resolver.createTypeOfDeclaration(node, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker)); + let typeNode; + switch (node.kind) { + case SyntaxKind.Parameter: + case SyntaxKind.PropertySignature: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.BindingElement: + case SyntaxKind.VariableDeclaration: + typeNode = resolver.createTypeOfDeclaration(node, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker, shouldAddImplicitUndefined); + break; + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ConstructSignature: + case SyntaxKind.MethodSignature: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.CallSignature: + typeNode = resolver.createReturnTypeOfSignatureDeclaration(node, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker); + break; + default: + Debug.assertNever(node); } - if ( - node.kind === SyntaxKind.Parameter - || node.kind === SyntaxKind.PropertyDeclaration - || node.kind === SyntaxKind.PropertySignature - ) { - if (isPropertySignature(node) || !node.initializer) return cleanup(resolver.createTypeOfDeclaration(node, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker, shouldUseResolverType)); - return cleanup(resolver.createTypeOfDeclaration(node, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker, shouldUseResolverType) || resolver.createTypeOfExpression(node.initializer, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker)); - } - return cleanup(resolver.createReturnTypeOfSignatureDeclaration(node, enclosingDeclaration, declarationEmitNodeBuilderFlags, symbolTracker)); - function cleanup(returnValue: TypeNode | undefined) { - errorNameNode = undefined; - if (!suppressNewDiagnosticContexts) { - getSymbolAccessibilityDiagnostic = oldDiag; - } - return returnValue || factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); + errorNameNode = undefined; + if (!suppressNewDiagnosticContexts) { + getSymbolAccessibilityDiagnostic = oldDiag!; } + return typeNode ?? factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } function isDeclarationAndNotVisible(node: NamedDeclaration) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ead7edab366..3d7b3580bcc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5603,8 +5603,7 @@ export interface EmitResolver { isLateBound(node: Declaration): node is LateBoundDeclaration; collectLinkedAliases(node: Identifier, setVisibility?: boolean): Node[] | undefined; isImplementationOfOverload(node: SignatureDeclaration): boolean | undefined; - isRequiredInitializedParameter(node: ParameterDeclaration): boolean; - isOptionalUninitializedParameterProperty(node: ParameterDeclaration): boolean; + requiresAddingImplicitUndefined(node: ParameterDeclaration): boolean; isExpandoFunctionDeclaration(node: FunctionDeclaration): boolean; getPropertiesOfContainerFunction(node: Declaration): Symbol[]; createTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration | PropertyAccessExpression | ElementAccessExpression | BinaryExpression, enclosingDeclaration: Node, flags: NodeBuilderFlags, tracker: SymbolTracker, addUndefined?: boolean): TypeNode | undefined; diff --git a/tests/baselines/reference/verbatim-declarations-parameters.js b/tests/baselines/reference/verbatim-declarations-parameters.js new file mode 100644 index 00000000000..d0d54ba7e5e --- /dev/null +++ b/tests/baselines/reference/verbatim-declarations-parameters.js @@ -0,0 +1,74 @@ +//// [tests/cases/compiler/verbatim-declarations-parameters.ts] //// + +//// [verbatim-declarations-parameters.ts] +type Map = {} & { [P in string]: any } +type MapOrUndefined = Map | undefined | "dummy" +export class Foo { + constructor( + // Type node is accurate, preserve + public reuseTypeNode?: Map | undefined, + public reuseTypeNode2?: Exclude, + // Resolve type node, requires adding | undefined + public resolveType?: Map, + ) { } +} + +export function foo1( + // Type node is accurate, preserve + reuseTypeNode: Map | undefined = {}, + reuseTypeNode2: Exclude = {}, + // Resolve type node, requires adding | undefined + resolveType: Map = {}, + requiredParam: number) { + +} + + +//// [verbatim-declarations-parameters.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.foo1 = exports.Foo = void 0; +var Foo = /** @class */ (function () { + function Foo( + // Type node is accurate, preserve + reuseTypeNode, reuseTypeNode2, + // Resolve type node, requires adding | undefined + resolveType) { + this.reuseTypeNode = reuseTypeNode; + this.reuseTypeNode2 = reuseTypeNode2; + this.resolveType = resolveType; + } + return Foo; +}()); +exports.Foo = Foo; +function foo1( +// Type node is accurate, preserve +reuseTypeNode, reuseTypeNode2, +// Resolve type node, requires adding | undefined +resolveType, requiredParam) { + if (reuseTypeNode === void 0) { reuseTypeNode = {}; } + if (reuseTypeNode2 === void 0) { reuseTypeNode2 = {}; } + if (resolveType === void 0) { resolveType = {}; } +} +exports.foo1 = foo1; + + +//// [verbatim-declarations-parameters.d.ts] +type Map = {} & { + [P in string]: any; +}; +type MapOrUndefined = Map | undefined | "dummy"; +export declare class Foo { + reuseTypeNode?: Map | undefined; + reuseTypeNode2?: Exclude; + resolveType?: { + [x: string]: any; + } | undefined; + constructor(reuseTypeNode?: Map | undefined, reuseTypeNode2?: Exclude, resolveType?: { + [x: string]: any; + } | undefined); +} +export declare function foo1(reuseTypeNode: Map | undefined, reuseTypeNode2: Exclude, resolveType: { + [x: string]: any; +} | undefined, requiredParam: number): void; +export {}; diff --git a/tests/baselines/reference/verbatim-declarations-parameters.symbols b/tests/baselines/reference/verbatim-declarations-parameters.symbols new file mode 100644 index 00000000000..f84ec29175c --- /dev/null +++ b/tests/baselines/reference/verbatim-declarations-parameters.symbols @@ -0,0 +1,56 @@ +//// [tests/cases/compiler/verbatim-declarations-parameters.ts] //// + +=== verbatim-declarations-parameters.ts === +type Map = {} & { [P in string]: any } +>Map : Symbol(Map, Decl(verbatim-declarations-parameters.ts, 0, 0)) +>P : Symbol(P, Decl(verbatim-declarations-parameters.ts, 0, 19)) + +type MapOrUndefined = Map | undefined | "dummy" +>MapOrUndefined : Symbol(MapOrUndefined, Decl(verbatim-declarations-parameters.ts, 0, 38)) +>Map : Symbol(Map, Decl(verbatim-declarations-parameters.ts, 0, 0)) + +export class Foo { +>Foo : Symbol(Foo, Decl(verbatim-declarations-parameters.ts, 1, 47)) + + constructor( + // Type node is accurate, preserve + public reuseTypeNode?: Map | undefined, +>reuseTypeNode : Symbol(Foo.reuseTypeNode, Decl(verbatim-declarations-parameters.ts, 3, 14)) +>Map : Symbol(Map, Decl(verbatim-declarations-parameters.ts, 0, 0)) + + public reuseTypeNode2?: Exclude, +>reuseTypeNode2 : Symbol(Foo.reuseTypeNode2, Decl(verbatim-declarations-parameters.ts, 5, 43)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>MapOrUndefined : Symbol(MapOrUndefined, Decl(verbatim-declarations-parameters.ts, 0, 38)) + + // Resolve type node, requires adding | undefined + public resolveType?: Map, +>resolveType : Symbol(Foo.resolveType, Decl(verbatim-declarations-parameters.ts, 6, 61)) +>Map : Symbol(Map, Decl(verbatim-declarations-parameters.ts, 0, 0)) + + ) { } +} + +export function foo1( +>foo1 : Symbol(foo1, Decl(verbatim-declarations-parameters.ts, 10, 1)) + + // Type node is accurate, preserve + reuseTypeNode: Map | undefined = {}, +>reuseTypeNode : Symbol(reuseTypeNode, Decl(verbatim-declarations-parameters.ts, 12, 21)) +>Map : Symbol(Map, Decl(verbatim-declarations-parameters.ts, 0, 0)) + + reuseTypeNode2: Exclude = {}, +>reuseTypeNode2 : Symbol(reuseTypeNode2, Decl(verbatim-declarations-parameters.ts, 14, 40)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>MapOrUndefined : Symbol(MapOrUndefined, Decl(verbatim-declarations-parameters.ts, 0, 38)) + + // Resolve type node, requires adding | undefined + resolveType: Map = {}, +>resolveType : Symbol(resolveType, Decl(verbatim-declarations-parameters.ts, 15, 59)) +>Map : Symbol(Map, Decl(verbatim-declarations-parameters.ts, 0, 0)) + + requiredParam: number) { +>requiredParam : Symbol(requiredParam, Decl(verbatim-declarations-parameters.ts, 17, 26)) + +} + diff --git a/tests/baselines/reference/verbatim-declarations-parameters.types b/tests/baselines/reference/verbatim-declarations-parameters.types new file mode 100644 index 00000000000..c22a7281300 --- /dev/null +++ b/tests/baselines/reference/verbatim-declarations-parameters.types @@ -0,0 +1,49 @@ +//// [tests/cases/compiler/verbatim-declarations-parameters.ts] //// + +=== verbatim-declarations-parameters.ts === +type Map = {} & { [P in string]: any } +>Map : { [x: string]: any; } + +type MapOrUndefined = Map | undefined | "dummy" +>MapOrUndefined : { [x: string]: any; } | "dummy" | undefined + +export class Foo { +>Foo : Foo + + constructor( + // Type node is accurate, preserve + public reuseTypeNode?: Map | undefined, +>reuseTypeNode : { [x: string]: any; } | undefined + + public reuseTypeNode2?: Exclude, +>reuseTypeNode2 : { [x: string]: any; } | undefined + + // Resolve type node, requires adding | undefined + public resolveType?: Map, +>resolveType : { [x: string]: any; } | undefined + + ) { } +} + +export function foo1( +>foo1 : (reuseTypeNode: Map | undefined, reuseTypeNode2: Exclude, resolveType: { [x: string]: any; } | undefined, requiredParam: number) => void + + // Type node is accurate, preserve + reuseTypeNode: Map | undefined = {}, +>reuseTypeNode : { [x: string]: any; } | undefined +>{} : {} + + reuseTypeNode2: Exclude = {}, +>reuseTypeNode2 : { [x: string]: any; } | undefined +>{} : {} + + // Resolve type node, requires adding | undefined + resolveType: Map = {}, +>resolveType : { [x: string]: any; } +>{} : {} + + requiredParam: number) { +>requiredParam : number + +} + diff --git a/tests/cases/compiler/verbatim-declarations-parameters.ts b/tests/cases/compiler/verbatim-declarations-parameters.ts new file mode 100644 index 00000000000..da1a6616356 --- /dev/null +++ b/tests/cases/compiler/verbatim-declarations-parameters.ts @@ -0,0 +1,24 @@ +// @strictNullChecks: true +// @declaration: true + +type Map = {} & { [P in string]: any } +type MapOrUndefined = Map | undefined | "dummy" +export class Foo { + constructor( + // Type node is accurate, preserve + public reuseTypeNode?: Map | undefined, + public reuseTypeNode2?: Exclude, + // Resolve type node, requires adding | undefined + public resolveType?: Map, + ) { } +} + +export function foo1( + // Type node is accurate, preserve + reuseTypeNode: Map | undefined = {}, + reuseTypeNode2: Exclude = {}, + // Resolve type node, requires adding | undefined + resolveType: Map = {}, + requiredParam: number) { + +}