diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a09b63d46fd0a..3517f407f870f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2694,7 +2694,11 @@ namespace ts { writePunctuation(writer, SyntaxKind.ColonToken); writeSpace(writer); - buildTypeDisplay(getTypeOfSymbol(p), writer, enclosingDeclaration, flags, symbolStack); + let type = getTypeOfSymbol(p); + if (isRequiredInitializedParameter(parameterNode)) { + type = includeFalsyTypes(type, TypeFlags.Undefined); + } + buildTypeDisplay(type, writer, enclosingDeclaration, flags, symbolStack); } function buildBindingPatternDisplay(bindingPattern: BindingPattern, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { @@ -3275,6 +3279,16 @@ namespace ts { return strictNullChecks && optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type; } + /** remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined) */ + function removeOptionalityFromAnnotation(annotatedType: Type, declaration: VariableLikeDeclaration): Type { + const annotationIncludesUndefined = strictNullChecks && + declaration.kind === SyntaxKind.Parameter && + declaration.initializer && + getFalsyFlags(annotatedType) & TypeFlags.Undefined && + !(getFalsyFlags(checkExpression(declaration.initializer)) & TypeFlags.Undefined); + return annotationIncludesUndefined ? getNonNullableType(annotatedType) : annotatedType; + } + // Return the inferred type for a variable, parameter, or property declaration function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type { if (declaration.flags & NodeFlags.JavaScriptFile) { @@ -3308,7 +3322,8 @@ namespace ts { // Use type from type annotation if one is present if (declaration.type) { - return addOptionality(getTypeFromTypeNode(declaration.type), /*optional*/ declaration.questionToken && includeOptionality); + const declaredType = removeOptionalityFromAnnotation(getTypeFromTypeNode(declaration.type), declaration); + return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality); } if ((compilerOptions.noImplicitAny || declaration.flags & NodeFlags.JavaScriptFile) && @@ -5133,6 +5148,12 @@ namespace ts { Debug.assert(parameterIndex >= 0); return parameterIndex >= signature.minArgumentCount; } + const iife = getImmediatelyInvokedFunctionExpression(node.parent); + if (iife) { + return !node.type && + !node.dotDotDotToken && + indexOf((node.parent as SignatureDeclaration).parameters, node) >= iife.arguments.length; + } return false; } @@ -20526,6 +20547,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; @@ -20617,10 +20645,12 @@ namespace ts { function writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter) { // Get type of the symbol if this is the valid symbol otherwise get type at location const symbol = getSymbolOfNode(declaration); - const type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature)) + let type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature)) ? getWidenedLiteralType(getTypeOfSymbol(symbol)) : unknownType; - + if (flags & TypeFormatFlags.AddUndefined) { + type = includeFalsyTypes(type, TypeFlags.Undefined); + } getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags); } @@ -20719,6 +20749,7 @@ namespace ts { isTopLevelValueImportEqualsWithEntityName, isDeclarationVisible, isImplementationOfOverload, + isRequiredInitializedParameter, writeTypeOfDeclaration, writeReturnTypeOfSignatureDeclaration, writeTypeOfExpression, diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 46df4fed6896d..de8be5a27e80b 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -322,13 +322,20 @@ 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); + const format = TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue | + (shouldUseResolverType ? TypeFormatFlags.AddUndefined : 0); + resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, format, writer); errorNameNode = undefined; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8b49af402cf6c..48feb6bb0922f 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; diff --git a/tests/baselines/reference/contextuallyTypedIife.types b/tests/baselines/reference/contextuallyTypedIife.types index 15038e7dd263a..676755fb34175 100644 --- a/tests/baselines/reference/contextuallyTypedIife.types +++ b/tests/baselines/reference/contextuallyTypedIife.types @@ -253,8 +253,8 @@ let eleven = (o => o.a(11))({ a: function(n) { return n; } }); // missing arguments (function(x, undefined) { return x; })(42); >(function(x, undefined) { return x; })(42) : number ->(function(x, undefined) { return x; }) : (x: number, undefined: any) => number ->function(x, undefined) { return x; } : (x: number, undefined: any) => number +>(function(x, undefined) { return x; }) : (x: number, undefined?: any) => number +>function(x, undefined) { return x; } : (x: number, undefined?: any) => number >x : number >undefined : any >x : number @@ -262,8 +262,8 @@ let eleven = (o => o.a(11))({ a: function(n) { return n; } }); ((x, y, z) => 42)(); >((x, y, z) => 42)() : number ->((x, y, z) => 42) : (x: any, y: any, z: any) => number ->(x, y, z) => 42 : (x: any, y: any, z: any) => number +>((x, y, z) => 42) : (x?: any, y?: any, z?: any) => number +>(x, y, z) => 42 : (x?: any, y?: any, z?: any) => number >x : any >y : any >z : any diff --git a/tests/baselines/reference/defaultParameterAddsUndefinedWithStrictNullChecks.js b/tests/baselines/reference/defaultParameterAddsUndefinedWithStrictNullChecks.js new file mode 100644 index 0000000000000..329c2fb19ce01 --- /dev/null +++ b/tests/baselines/reference/defaultParameterAddsUndefinedWithStrictNullChecks.js @@ -0,0 +1,112 @@ +//// [defaultParameterAddsUndefinedWithStrictNullChecks.ts] +function f(addUndefined1 = "J", addUndefined2?: number) { + return addUndefined1.length + (addUndefined2 || 0); +} +function g(addUndefined = "J", addDefined: number) { + return addUndefined.length + addDefined; +} +let total = f() + f('a', 1) + f('b') + f(undefined, 2); +total = g('c', 3) + g(undefined, 4); + +function foo1(x: string = "string", b: number) { + x.length; +} + +function foo2(x = "string", b: number) { + x.length; // ok, should be string +} + +function foo3(x: string | undefined = "string", b: number) { + x.length; // ok, should be string +} + +function foo4(x: string | undefined = undefined, b: number) { + x; // should be string | undefined +} + + + +// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4 +foo1(undefined, 1); +foo2(undefined, 1); +foo3(undefined, 1); +foo4(undefined, 1); + + +function removeUndefinedButNotFalse(x = true) { + if (x === false) { + return x; + } +} + +declare const cond: boolean; +function removeNothing(y = cond ? true : undefined) { + if (y !== undefined) { + if (y === false) { + return y; + } + } + return true; +} + + +//// [defaultParameterAddsUndefinedWithStrictNullChecks.js] +function f(addUndefined1, addUndefined2) { + if (addUndefined1 === void 0) { addUndefined1 = "J"; } + return addUndefined1.length + (addUndefined2 || 0); +} +function g(addUndefined, addDefined) { + if (addUndefined === void 0) { addUndefined = "J"; } + return addUndefined.length + addDefined; +} +var total = f() + f('a', 1) + f('b') + f(undefined, 2); +total = g('c', 3) + g(undefined, 4); +function foo1(x, b) { + if (x === void 0) { x = "string"; } + x.length; +} +function foo2(x, b) { + if (x === void 0) { x = "string"; } + x.length; // ok, should be string +} +function foo3(x, b) { + if (x === void 0) { x = "string"; } + x.length; // ok, should be string +} +function foo4(x, b) { + if (x === void 0) { x = undefined; } + x; // should be string | undefined +} +// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4 +foo1(undefined, 1); +foo2(undefined, 1); +foo3(undefined, 1); +foo4(undefined, 1); +function removeUndefinedButNotFalse(x) { + if (x === void 0) { x = true; } + if (x === false) { + return x; + } +} +function removeNothing(y) { + if (y === void 0) { y = cond ? true : undefined; } + if (y !== undefined) { + if (y === false) { + return y; + } + } + return true; +} + + +//// [defaultParameterAddsUndefinedWithStrictNullChecks.d.ts] +declare function f(addUndefined1?: string, addUndefined2?: number): number; +declare function g(addUndefined: string | undefined, addDefined: number): number; +declare let total: number; +declare function foo1(x: string | undefined, b: number): void; +declare function foo2(x: string | undefined, b: number): void; +declare function foo3(x: string | undefined, b: number): void; +declare function foo4(x: string | undefined, b: number): void; +declare function removeUndefinedButNotFalse(x?: boolean): false | undefined; +declare const cond: boolean; +declare function removeNothing(y?: boolean | undefined): boolean; diff --git a/tests/baselines/reference/defaultParameterAddsUndefinedWithStrictNullChecks.symbols b/tests/baselines/reference/defaultParameterAddsUndefinedWithStrictNullChecks.symbols new file mode 100644 index 0000000000000..fb0fce7dc7f1a --- /dev/null +++ b/tests/baselines/reference/defaultParameterAddsUndefinedWithStrictNullChecks.symbols @@ -0,0 +1,135 @@ +=== tests/cases/compiler/defaultParameterAddsUndefinedWithStrictNullChecks.ts === +function f(addUndefined1 = "J", addUndefined2?: number) { +>f : Symbol(f, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 0, 0)) +>addUndefined1 : Symbol(addUndefined1, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 0, 11)) +>addUndefined2 : Symbol(addUndefined2, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 0, 31)) + + return addUndefined1.length + (addUndefined2 || 0); +>addUndefined1.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>addUndefined1 : Symbol(addUndefined1, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 0, 11)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>addUndefined2 : Symbol(addUndefined2, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 0, 31)) +} +function g(addUndefined = "J", addDefined: number) { +>g : Symbol(g, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 2, 1)) +>addUndefined : Symbol(addUndefined, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 3, 11)) +>addDefined : Symbol(addDefined, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 3, 30)) + + return addUndefined.length + addDefined; +>addUndefined.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>addUndefined : Symbol(addUndefined, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 3, 11)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>addDefined : Symbol(addDefined, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 3, 30)) +} +let total = f() + f('a', 1) + f('b') + f(undefined, 2); +>total : Symbol(total, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 6, 3)) +>f : Symbol(f, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 0, 0)) +>f : Symbol(f, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 0, 0)) +>f : Symbol(f, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 0, 0)) +>f : Symbol(f, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 0, 0)) +>undefined : Symbol(undefined) + +total = g('c', 3) + g(undefined, 4); +>total : Symbol(total, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 6, 3)) +>g : Symbol(g, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 2, 1)) +>g : Symbol(g, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 2, 1)) +>undefined : Symbol(undefined) + +function foo1(x: string = "string", b: number) { +>foo1 : Symbol(foo1, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 7, 36)) +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 9, 14)) +>b : Symbol(b, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 9, 35)) + + x.length; +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 9, 14)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +} + +function foo2(x = "string", b: number) { +>foo2 : Symbol(foo2, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 11, 1)) +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 13, 14)) +>b : Symbol(b, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 13, 27)) + + x.length; // ok, should be string +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 13, 14)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +} + +function foo3(x: string | undefined = "string", b: number) { +>foo3 : Symbol(foo3, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 15, 1)) +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 17, 14)) +>b : Symbol(b, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 17, 47)) + + x.length; // ok, should be string +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 17, 14)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +} + +function foo4(x: string | undefined = undefined, b: number) { +>foo4 : Symbol(foo4, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 19, 1)) +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 21, 14)) +>undefined : Symbol(undefined) +>b : Symbol(b, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 21, 48)) + + x; // should be string | undefined +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 21, 14)) +} + + + +// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4 +foo1(undefined, 1); +>foo1 : Symbol(foo1, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 7, 36)) +>undefined : Symbol(undefined) + +foo2(undefined, 1); +>foo2 : Symbol(foo2, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 11, 1)) +>undefined : Symbol(undefined) + +foo3(undefined, 1); +>foo3 : Symbol(foo3, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 15, 1)) +>undefined : Symbol(undefined) + +foo4(undefined, 1); +>foo4 : Symbol(foo4, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 19, 1)) +>undefined : Symbol(undefined) + + +function removeUndefinedButNotFalse(x = true) { +>removeUndefinedButNotFalse : Symbol(removeUndefinedButNotFalse, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 31, 19)) +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 34, 36)) + + if (x === false) { +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 34, 36)) + + return x; +>x : Symbol(x, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 34, 36)) + } +} + +declare const cond: boolean; +>cond : Symbol(cond, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 40, 13)) + +function removeNothing(y = cond ? true : undefined) { +>removeNothing : Symbol(removeNothing, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 40, 28)) +>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 41, 23)) +>cond : Symbol(cond, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 40, 13)) +>undefined : Symbol(undefined) + + if (y !== undefined) { +>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 41, 23)) +>undefined : Symbol(undefined) + + if (y === false) { +>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 41, 23)) + + return y; +>y : Symbol(y, Decl(defaultParameterAddsUndefinedWithStrictNullChecks.ts, 41, 23)) + } + } + return true; +} + diff --git a/tests/baselines/reference/defaultParameterAddsUndefinedWithStrictNullChecks.types b/tests/baselines/reference/defaultParameterAddsUndefinedWithStrictNullChecks.types new file mode 100644 index 0000000000000..d95f925989173 --- /dev/null +++ b/tests/baselines/reference/defaultParameterAddsUndefinedWithStrictNullChecks.types @@ -0,0 +1,180 @@ +=== tests/cases/compiler/defaultParameterAddsUndefinedWithStrictNullChecks.ts === +function f(addUndefined1 = "J", addUndefined2?: number) { +>f : (addUndefined1?: string, addUndefined2?: number | undefined) => number +>addUndefined1 : string +>"J" : "J" +>addUndefined2 : number | undefined + + return addUndefined1.length + (addUndefined2 || 0); +>addUndefined1.length + (addUndefined2 || 0) : number +>addUndefined1.length : number +>addUndefined1 : string +>length : number +>(addUndefined2 || 0) : number +>addUndefined2 || 0 : number +>addUndefined2 : number | undefined +>0 : 0 +} +function g(addUndefined = "J", addDefined: number) { +>g : (addUndefined: string | undefined, addDefined: number) => number +>addUndefined : string +>"J" : "J" +>addDefined : number + + return addUndefined.length + addDefined; +>addUndefined.length + addDefined : number +>addUndefined.length : number +>addUndefined : string +>length : number +>addDefined : number +} +let total = f() + f('a', 1) + f('b') + f(undefined, 2); +>total : number +>f() + f('a', 1) + f('b') + f(undefined, 2) : number +>f() + f('a', 1) + f('b') : number +>f() + f('a', 1) : number +>f() : number +>f : (addUndefined1?: string, addUndefined2?: number | undefined) => number +>f('a', 1) : number +>f : (addUndefined1?: string, addUndefined2?: number | undefined) => number +>'a' : "a" +>1 : 1 +>f('b') : number +>f : (addUndefined1?: string, addUndefined2?: number | undefined) => number +>'b' : "b" +>f(undefined, 2) : number +>f : (addUndefined1?: string, addUndefined2?: number | undefined) => number +>undefined : undefined +>2 : 2 + +total = g('c', 3) + g(undefined, 4); +>total = g('c', 3) + g(undefined, 4) : number +>total : number +>g('c', 3) + g(undefined, 4) : number +>g('c', 3) : number +>g : (addUndefined: string | undefined, addDefined: number) => number +>'c' : "c" +>3 : 3 +>g(undefined, 4) : number +>g : (addUndefined: string | undefined, addDefined: number) => number +>undefined : undefined +>4 : 4 + +function foo1(x: string = "string", b: number) { +>foo1 : (x: string | undefined, b: number) => void +>x : string +>"string" : "string" +>b : number + + x.length; +>x.length : number +>x : string +>length : number +} + +function foo2(x = "string", b: number) { +>foo2 : (x: string | undefined, b: number) => void +>x : string +>"string" : "string" +>b : number + + x.length; // ok, should be string +>x.length : number +>x : string +>length : number +} + +function foo3(x: string | undefined = "string", b: number) { +>foo3 : (x: string | undefined, b: number) => void +>x : string +>"string" : "string" +>b : number + + x.length; // ok, should be string +>x.length : number +>x : string +>length : number +} + +function foo4(x: string | undefined = undefined, b: number) { +>foo4 : (x: string | undefined, b: number) => void +>x : string | undefined +>undefined : undefined +>b : number + + x; // should be string | undefined +>x : string | undefined +} + + + +// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4 +foo1(undefined, 1); +>foo1(undefined, 1) : void +>foo1 : (x: string | undefined, b: number) => void +>undefined : undefined +>1 : 1 + +foo2(undefined, 1); +>foo2(undefined, 1) : void +>foo2 : (x: string | undefined, b: number) => void +>undefined : undefined +>1 : 1 + +foo3(undefined, 1); +>foo3(undefined, 1) : void +>foo3 : (x: string | undefined, b: number) => void +>undefined : undefined +>1 : 1 + +foo4(undefined, 1); +>foo4(undefined, 1) : void +>foo4 : (x: string | undefined, b: number) => void +>undefined : undefined +>1 : 1 + + +function removeUndefinedButNotFalse(x = true) { +>removeUndefinedButNotFalse : (x?: boolean) => false | undefined +>x : boolean +>true : true + + if (x === false) { +>x === false : boolean +>x : boolean +>false : false + + return x; +>x : false + } +} + +declare const cond: boolean; +>cond : boolean + +function removeNothing(y = cond ? true : undefined) { +>removeNothing : (y?: boolean | undefined) => boolean +>y : boolean | undefined +>cond ? true : undefined : true | undefined +>cond : boolean +>true : true +>undefined : undefined + + if (y !== undefined) { +>y !== undefined : boolean +>y : boolean | undefined +>undefined : undefined + + if (y === false) { +>y === false : boolean +>y : boolean +>false : false + + return y; +>y : false + } + } + return true; +>true : true +} + diff --git a/tests/cases/compiler/defaultParameterAddsUndefinedWithStrictNullChecks.ts b/tests/cases/compiler/defaultParameterAddsUndefinedWithStrictNullChecks.ts new file mode 100644 index 0000000000000..8abea19f6cd35 --- /dev/null +++ b/tests/cases/compiler/defaultParameterAddsUndefinedWithStrictNullChecks.ts @@ -0,0 +1,51 @@ +// @strictNullChecks: true +// @declaration: true +function f(addUndefined1 = "J", addUndefined2?: number) { + return addUndefined1.length + (addUndefined2 || 0); +} +function g(addUndefined = "J", addDefined: number) { + return addUndefined.length + addDefined; +} +let total = f() + f('a', 1) + f('b') + f(undefined, 2); +total = g('c', 3) + g(undefined, 4); + +function foo1(x: string = "string", b: number) { + x.length; +} + +function foo2(x = "string", b: number) { + x.length; // ok, should be string +} + +function foo3(x: string | undefined = "string", b: number) { + x.length; // ok, should be string +} + +function foo4(x: string | undefined = undefined, b: number) { + x; // should be string | undefined +} + + + +// .d.ts should have `string | undefined` for foo1, foo2, foo3 and foo4 +foo1(undefined, 1); +foo2(undefined, 1); +foo3(undefined, 1); +foo4(undefined, 1); + + +function removeUndefinedButNotFalse(x = true) { + if (x === false) { + return x; + } +} + +declare const cond: boolean; +function removeNothing(y = cond ? true : undefined) { + if (y !== undefined) { + if (y === false) { + return y; + } + } + return true; +} diff --git a/tests/cases/fourslash/quickInfoDisplayPartsIife.ts b/tests/cases/fourslash/quickInfoDisplayPartsIife.ts new file mode 100644 index 0000000000000..e4ce2bf96e3ec --- /dev/null +++ b/tests/cases/fourslash/quickInfoDisplayPartsIife.ts @@ -0,0 +1,5 @@ +/// +// @strictNullChecks: true +////var iife = (function foo/*1*/(x, y) { return x })(12); + +verify.quickInfoAt('1', '(local function) foo(x: number, y?: undefined): number');