diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b7dd84f7716..db0e4e1575d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23008,6 +23008,13 @@ namespace ts { !(getModifierFlags(parameter) & ModifierFlags.ParameterPropertyModifier); } + function isOptionalUninitializedParameterProperty(parameter: ParameterDeclaration) { + return strictNullChecks && + isOptionalParameter(parameter) && + !parameter.initializer && + !!(getModifierFlags(parameter) & ModifierFlags.ParameterPropertyModifier); + } + function getNodeCheckFlags(node: Node): NodeCheckFlags { return getNodeLinks(node).flags; } @@ -23217,6 +23224,7 @@ namespace ts { isDeclarationVisible, isImplementationOfOverload, isRequiredInitializedParameter, + isOptionalUninitializedParameterProperty, writeTypeOfDeclaration, writeReturnTypeOfSignatureDeclaration, writeTypeOfExpression, diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 007620ad510..753080cc6a0 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -335,9 +335,12 @@ namespace ts { write(": "); // use the checker's type, not the declared type, - // for non-optional initialized parameters that aren't a parameter property + // for optional parameter properties + // and also for non-optional initialized parameters that aren't a parameter property + // these types may need to add `undefined`. const shouldUseResolverType = declaration.kind === SyntaxKind.Parameter && - resolver.isRequiredInitializedParameter(declaration as ParameterDeclaration); + (resolver.isRequiredInitializedParameter(declaration as ParameterDeclaration) || + resolver.isOptionalUninitializedParameterProperty(declaration as ParameterDeclaration)); if (type && !shouldUseResolverType) { // Write the type emitType(type); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 75eabac49b0..76be7b20b1d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2814,6 +2814,7 @@ namespace ts { collectLinkedAliases(node: Identifier): Node[]; isImplementationOfOverload(node: FunctionLikeDeclaration): boolean | undefined; isRequiredInitializedParameter(node: ParameterDeclaration): boolean; + isOptionalUninitializedParameterProperty(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; diff --git a/tests/baselines/reference/declarationEmitParameterProperty.js b/tests/baselines/reference/declarationEmitParameterProperty.js new file mode 100644 index 00000000000..262222e3c68 --- /dev/null +++ b/tests/baselines/reference/declarationEmitParameterProperty.js @@ -0,0 +1,24 @@ +//// [declarationEmitParameterProperty.ts] +export class Foo { + constructor(public bar?: string) { + } +} + + +//// [declarationEmitParameterProperty.js] +"use strict"; +exports.__esModule = true; +var Foo = (function () { + function Foo(bar) { + this.bar = bar; + } + return Foo; +}()); +exports.Foo = Foo; + + +//// [declarationEmitParameterProperty.d.ts] +export declare class Foo { + bar: string | undefined; + constructor(bar?: string | undefined); +} diff --git a/tests/baselines/reference/declarationEmitParameterProperty.symbols b/tests/baselines/reference/declarationEmitParameterProperty.symbols new file mode 100644 index 00000000000..d9e88e52cb0 --- /dev/null +++ b/tests/baselines/reference/declarationEmitParameterProperty.symbols @@ -0,0 +1,9 @@ +=== tests/cases/compiler/declarationEmitParameterProperty.ts === +export class Foo { +>Foo : Symbol(Foo, Decl(declarationEmitParameterProperty.ts, 0, 0)) + + constructor(public bar?: string) { +>bar : Symbol(Foo.bar, Decl(declarationEmitParameterProperty.ts, 1, 14)) + } +} + diff --git a/tests/baselines/reference/declarationEmitParameterProperty.types b/tests/baselines/reference/declarationEmitParameterProperty.types new file mode 100644 index 00000000000..3adfecf0c75 --- /dev/null +++ b/tests/baselines/reference/declarationEmitParameterProperty.types @@ -0,0 +1,9 @@ +=== tests/cases/compiler/declarationEmitParameterProperty.ts === +export class Foo { +>Foo : Foo + + constructor(public bar?: string) { +>bar : string | undefined + } +} + diff --git a/tests/baselines/reference/optionalMethods.js b/tests/baselines/reference/optionalMethods.js index d219466c562..2abcd209277 100644 --- a/tests/baselines/reference/optionalMethods.js +++ b/tests/baselines/reference/optionalMethods.js @@ -131,12 +131,12 @@ interface Foo { } declare function test1(x: Foo): void; declare class Bar { - d: number; + d: number | undefined; e: number; a: number; b?: number; c?: number | undefined; - constructor(d?: number, e?: number); + constructor(d?: number | undefined, e?: number); f(): number; g?(): number; h?(): number; diff --git a/tests/cases/compiler/declarationEmitParameterProperty.ts b/tests/cases/compiler/declarationEmitParameterProperty.ts new file mode 100644 index 00000000000..107d192fe88 --- /dev/null +++ b/tests/cases/compiler/declarationEmitParameterProperty.ts @@ -0,0 +1,6 @@ +// @strictNullChecks: true +// @declaration: true +export class Foo { + constructor(public bar?: string) { + } +}