diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8ba4d7d896f8b..33c188b13883c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -885,6 +885,7 @@ import { ObjectLiteralElementLike, ObjectLiteralExpression, ObjectType, + OmittedExpression, OptionalChain, OptionalTypeNode, or, @@ -2241,6 +2242,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var potentialReflectCollisions: Node[] = []; var potentialUnusedRenamedBindingElementsInTypes: BindingElement[] = []; var awaitedTypeStack: number[] = []; + var ambiguousVariableDeclarationsWhenWidened: [node: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement, type: Type, widenedLiteralType: Type][] = []; var diagnostics = createDiagnosticCollection(); var suggestionDiagnostics = createDiagnosticCollection(); @@ -43141,6 +43143,40 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } checkCollisionsForDeclarationName(node, node.name); } + + // TODO(jakebailey): copy pasted from declaration.ts; improve + type CanHaveLiteralInitializer = VariableDeclaration | PropertyDeclaration | PropertySignature | ParameterDeclaration; + function canHaveLiteralInitializer(node: Node): boolean { + switch (node.kind) { + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + return !hasEffectiveModifier(node, ModifierFlags.Private); + case SyntaxKind.Parameter: + case SyntaxKind.VariableDeclaration: + return true; + } + return false; + } + + function shouldPrintWithInitializer(node: Node) { + return canHaveLiteralInitializer(node) && isLiteralConstDeclaration(node as CanHaveLiteralInitializer); // TODO: Make safe + } + + function isPrivateDeclaration(node: Node) { + return hasEffectiveModifier(node, ModifierFlags.Private) || isPrivateIdentifierClassElementDeclaration(node); + } + + if ( + getEmitDeclarations(compilerOptions) + && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature)) + && !shouldPrintWithInitializer(node) + && !isPrivateDeclaration(node) + ) { + const widenedLiteralType = getWidenedLiteralType(type); + if (!isTypeIdenticalTo(type, widenedLiteralType)) { + ambiguousVariableDeclarationsWhenWidened.push([node, type, widenedLiteralType]); + } + } } function errorNextVariableOrPropertyDeclarationMustHaveSameType(firstDeclaration: Declaration | undefined, firstType: Type, nextDeclaration: Declaration, nextType: Type): void { @@ -47243,6 +47279,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { clear(potentialWeakMapSetCollisions); clear(potentialReflectCollisions); clear(potentialUnusedRenamedBindingElementsInTypes); + clear(ambiguousVariableDeclarationsWhenWidened); forEach(node.statements, checkSourceElement); checkSourceElement(node.endOfFileToken); @@ -47267,6 +47304,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } }); + addLazyDiagnostic(() => { + function isVisibleExternally(elem: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | OmittedExpression): boolean { + if (isOmittedExpression(elem)) { + return false; + } + if (isBindingPattern(elem.name)) { + return some(elem.name.elements, isVisibleExternally); + } + else { + return isDeclarationVisible(elem); + } + } + + for (const [node, type, widenedLiteralType] of ambiguousVariableDeclarationsWhenWidened) { + if (isVisibleExternally(node)) { + error(node.name, Diagnostics.The_type_of_this_declaration_is_ambiguous_and_may_be_observed_as_either_0_or_1, typeToString(widenedLiteralType), typeToString(type)); + } + } + }); + if (isExternalOrCommonJsModule(node)) { checkExternalModuleExports(node); } @@ -47291,6 +47348,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { clear(potentialReflectCollisions); } + if (ambiguousVariableDeclarationsWhenWidened.length) { + // forEach(potentialAmbiguousDeclarationsWhenWidened, checkAmbiguousDeclaration); + clear(ambiguousVariableDeclarationsWhenWidened); + } + links.flags |= NodeCheckFlags.TypeChecked; } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 4c48402afafbb..d8c46814b3918 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3755,6 +3755,10 @@ "category": "Error", "code": 2868 }, + "The type of this declaration is ambiguous and may be observed as either '{0}' or '{1}'.": { + "category": "Error", + "code": 2869 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/tests/baselines/reference/ambientConstLiterals.errors.txt b/tests/baselines/reference/ambientConstLiterals.errors.txt new file mode 100644 index 0000000000000..4704da7ebd7f2 --- /dev/null +++ b/tests/baselines/reference/ambientConstLiterals.errors.txt @@ -0,0 +1,30 @@ +ambientConstLiterals.ts(20,7): error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"abc" | "def"'. +ambientConstLiterals.ts(21,7): error TS2869: The type of this declaration is ambiguous and may be observed as either 'number' or '123 | 456'. + + +==== ambientConstLiterals.ts (2 errors) ==== + function f(x: T): T { + return x; + } + + enum E { A, B, C, "non identifier" } + + const c1 = "abc"; + const c2 = 123; + const c3 = c1; + const c4 = c2; + const c5 = f(123); + const c6 = f(-123); + const c7 = true; + const c8 = E.A; + const c8b = E["non identifier"]; + const c9 = { x: "abc" }; + const c10 = [123]; + const c11 = "abc" + "def"; + const c12 = 123 + 456; + const c13 = Math.random() > 0.5 ? "abc" : "def"; + ~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"abc" | "def"'. + const c14 = Math.random() > 0.5 ? 123 : 456; + ~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'number' or '123 | 456'. \ No newline at end of file diff --git a/tests/baselines/reference/ambiguousLiteralWideningEmit.js b/tests/baselines/reference/ambiguousLiteralWideningEmit.js new file mode 100644 index 0000000000000..400623c80543d --- /dev/null +++ b/tests/baselines/reference/ambiguousLiteralWideningEmit.js @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/ambiguousLiteralWideningEmit.ts] //// + +//// [ambiguousLiteralWideningEmit.ts] +declare function pad(n: number | string): string; + +export default (dateString: string, type: 'date' | 'month' | 'year'): string => { + const [year, month = 1, date = 1] = dateString.split('-') + return `${year}-${pad(month)}-${pad(date)}`.substr(0, { date: 10, month: 7, year: 4 }[type]) +} + + +//// [ambiguousLiteralWideningEmit.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = (function (dateString, type) { + var _a = dateString.split('-'), year = _a[0], _b = _a[1], month = _b === void 0 ? 1 : _b, _c = _a[2], date = _c === void 0 ? 1 : _c; + return "".concat(year, "-").concat(pad(month), "-").concat(pad(date)).substr(0, { date: 10, month: 7, year: 4 }[type]); +}); + + +//// [ambiguousLiteralWideningEmit.d.ts] +declare const _default: (dateString: string, type: 'date' | 'month' | 'year') => string; +export default _default; diff --git a/tests/baselines/reference/ambiguousLiteralWideningEmit.symbols b/tests/baselines/reference/ambiguousLiteralWideningEmit.symbols new file mode 100644 index 0000000000000..c9c98bf17cf50 --- /dev/null +++ b/tests/baselines/reference/ambiguousLiteralWideningEmit.symbols @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/ambiguousLiteralWideningEmit.ts] //// + +=== ambiguousLiteralWideningEmit.ts === +declare function pad(n: number | string): string; +>pad : Symbol(pad, Decl(ambiguousLiteralWideningEmit.ts, 0, 0)) +>n : Symbol(n, Decl(ambiguousLiteralWideningEmit.ts, 0, 21)) + +export default (dateString: string, type: 'date' | 'month' | 'year'): string => { +>dateString : Symbol(dateString, Decl(ambiguousLiteralWideningEmit.ts, 2, 16)) +>type : Symbol(type, Decl(ambiguousLiteralWideningEmit.ts, 2, 35)) + + const [year, month = 1, date = 1] = dateString.split('-') +>year : Symbol(year, Decl(ambiguousLiteralWideningEmit.ts, 3, 11)) +>month : Symbol(month, Decl(ambiguousLiteralWideningEmit.ts, 3, 16)) +>date : Symbol(date, Decl(ambiguousLiteralWideningEmit.ts, 3, 27)) +>dateString.split : Symbol(String.split, Decl(lib.es5.d.ts, --, --)) +>dateString : Symbol(dateString, Decl(ambiguousLiteralWideningEmit.ts, 2, 16)) +>split : Symbol(String.split, Decl(lib.es5.d.ts, --, --)) + + return `${year}-${pad(month)}-${pad(date)}`.substr(0, { date: 10, month: 7, year: 4 }[type]) +>`${year}-${pad(month)}-${pad(date)}`.substr : Symbol(String.substr, Decl(lib.es5.d.ts, --, --)) +>year : Symbol(year, Decl(ambiguousLiteralWideningEmit.ts, 3, 11)) +>pad : Symbol(pad, Decl(ambiguousLiteralWideningEmit.ts, 0, 0)) +>month : Symbol(month, Decl(ambiguousLiteralWideningEmit.ts, 3, 16)) +>pad : Symbol(pad, Decl(ambiguousLiteralWideningEmit.ts, 0, 0)) +>date : Symbol(date, Decl(ambiguousLiteralWideningEmit.ts, 3, 27)) +>substr : Symbol(String.substr, Decl(lib.es5.d.ts, --, --)) +>date : Symbol(date, Decl(ambiguousLiteralWideningEmit.ts, 4, 59)) +>month : Symbol(month, Decl(ambiguousLiteralWideningEmit.ts, 4, 69)) +>year : Symbol(year, Decl(ambiguousLiteralWideningEmit.ts, 4, 79)) +>type : Symbol(type, Decl(ambiguousLiteralWideningEmit.ts, 2, 35)) +} + diff --git a/tests/baselines/reference/ambiguousLiteralWideningEmit.types b/tests/baselines/reference/ambiguousLiteralWideningEmit.types new file mode 100644 index 0000000000000..d2c6ad50a50b8 --- /dev/null +++ b/tests/baselines/reference/ambiguousLiteralWideningEmit.types @@ -0,0 +1,48 @@ +//// [tests/cases/compiler/ambiguousLiteralWideningEmit.ts] //// + +=== ambiguousLiteralWideningEmit.ts === +declare function pad(n: number | string): string; +>pad : (n: number | string) => string +>n : string | number + +export default (dateString: string, type: 'date' | 'month' | 'year'): string => { +>(dateString: string, type: 'date' | 'month' | 'year'): string => { const [year, month = 1, date = 1] = dateString.split('-') return `${year}-${pad(month)}-${pad(date)}`.substr(0, { date: 10, month: 7, year: 4 }[type])} : (dateString: string, type: 'date' | 'month' | 'year') => string +>dateString : string +>type : "date" | "month" | "year" + + const [year, month = 1, date = 1] = dateString.split('-') +>year : string +>month : string | 1 +>1 : 1 +>date : string | 1 +>1 : 1 +>dateString.split('-') : string[] +>dateString.split : (separator: string | RegExp, limit?: number | undefined) => string[] +>dateString : string +>split : (separator: string | RegExp, limit?: number | undefined) => string[] +>'-' : "-" + + return `${year}-${pad(month)}-${pad(date)}`.substr(0, { date: 10, month: 7, year: 4 }[type]) +>`${year}-${pad(month)}-${pad(date)}`.substr(0, { date: 10, month: 7, year: 4 }[type]) : string +>`${year}-${pad(month)}-${pad(date)}`.substr : (from: number, length?: number | undefined) => string +>`${year}-${pad(month)}-${pad(date)}` : string +>year : string +>pad(month) : string +>pad : (n: string | number) => string +>month : string | 1 +>pad(date) : string +>pad : (n: string | number) => string +>date : string | 1 +>substr : (from: number, length?: number | undefined) => string +>0 : 0 +>{ date: 10, month: 7, year: 4 }[type] : number +>{ date: 10, month: 7, year: 4 } : { date: number; month: number; year: number; } +>date : number +>10 : 10 +>month : number +>7 : 7 +>year : number +>4 : 4 +>type : "date" | "month" | "year" +} + diff --git a/tests/baselines/reference/declarationEmitBindingPatterns2.errors.txt b/tests/baselines/reference/declarationEmitBindingPatterns2.errors.txt new file mode 100644 index 0000000000000..a276b4e7f4586 --- /dev/null +++ b/tests/baselines/reference/declarationEmitBindingPatterns2.errors.txt @@ -0,0 +1,14 @@ +declarationEmitBindingPatterns2.ts(7,16): error TS2869: The type of this declaration is ambiguous and may be observed as either 'number' or '1 | 0'. + + +==== declarationEmitBindingPatterns2.ts (1 errors) ==== + // https://github.com/microsoft/TypeScript/issues/55439 + + function foo(): { y: 1 } { + return { y: 1 }; + } + + export const { y = 0 } = foo(); + ~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'number' or '1 | 0'. + \ No newline at end of file diff --git a/tests/baselines/reference/declarationEmitBindingPatterns2.js b/tests/baselines/reference/declarationEmitBindingPatterns2.js new file mode 100644 index 0000000000000..8abcd2dd9a052 --- /dev/null +++ b/tests/baselines/reference/declarationEmitBindingPatterns2.js @@ -0,0 +1,16 @@ +//// [tests/cases/compiler/declarationEmitBindingPatterns2.ts] //// + +//// [declarationEmitBindingPatterns2.ts] +// https://github.com/microsoft/TypeScript/issues/55439 + +function foo(): { y: 1 } { + return { y: 1 }; +} + +export const { y = 0 } = foo(); + + + + +//// [declarationEmitBindingPatterns2.d.ts] +export declare const y: number; diff --git a/tests/baselines/reference/declarationEmitBindingPatterns2.symbols b/tests/baselines/reference/declarationEmitBindingPatterns2.symbols new file mode 100644 index 0000000000000..1e34957ac35cc --- /dev/null +++ b/tests/baselines/reference/declarationEmitBindingPatterns2.symbols @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/declarationEmitBindingPatterns2.ts] //// + +=== declarationEmitBindingPatterns2.ts === +// https://github.com/microsoft/TypeScript/issues/55439 + +function foo(): { y: 1 } { +>foo : Symbol(foo, Decl(declarationEmitBindingPatterns2.ts, 0, 0)) +>y : Symbol(y, Decl(declarationEmitBindingPatterns2.ts, 2, 17)) + + return { y: 1 }; +>y : Symbol(y, Decl(declarationEmitBindingPatterns2.ts, 3, 10)) +} + +export const { y = 0 } = foo(); +>y : Symbol(y, Decl(declarationEmitBindingPatterns2.ts, 6, 14)) +>foo : Symbol(foo, Decl(declarationEmitBindingPatterns2.ts, 0, 0)) + diff --git a/tests/baselines/reference/declarationEmitBindingPatterns2.types b/tests/baselines/reference/declarationEmitBindingPatterns2.types new file mode 100644 index 0000000000000..dbc15e51059c1 --- /dev/null +++ b/tests/baselines/reference/declarationEmitBindingPatterns2.types @@ -0,0 +1,21 @@ +//// [tests/cases/compiler/declarationEmitBindingPatterns2.ts] //// + +=== declarationEmitBindingPatterns2.ts === +// https://github.com/microsoft/TypeScript/issues/55439 + +function foo(): { y: 1 } { +>foo : () => { y: 1;} +>y : 1 + + return { y: 1 }; +>{ y: 1 } : { y: 1; } +>y : 1 +>1 : 1 +} + +export const { y = 0 } = foo(); +>y : 1 | 0 +>0 : 0 +>foo() : { y: 1; } +>foo : () => { y: 1; } + diff --git a/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.js b/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.js index ed33b7fe09199..ea53c945e8d4d 100644 --- a/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.js +++ b/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.js @@ -53,3 +53,53 @@ export declare const SpotifyAgeGroupEnum: { "60-150": (typeof AgeGroups)["60-150"]; }; export {}; + + +//// [DtsFileErrors] + + +declarationEmitSpreadStringlyKeyedEnum.d.ts(12,5): error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["0-17"]'. +declarationEmitSpreadStringlyKeyedEnum.d.ts(13,5): error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["18-22"]'. +declarationEmitSpreadStringlyKeyedEnum.d.ts(14,5): error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["23-27"]'. +declarationEmitSpreadStringlyKeyedEnum.d.ts(15,5): error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["28-34"]'. +declarationEmitSpreadStringlyKeyedEnum.d.ts(16,5): error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["35-44"]'. +declarationEmitSpreadStringlyKeyedEnum.d.ts(17,5): error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["45-59"]'. +declarationEmitSpreadStringlyKeyedEnum.d.ts(18,5): error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["60-150"]'. + + +==== declarationEmitSpreadStringlyKeyedEnum.d.ts (7 errors) ==== + declare enum AgeGroups { + "0-17" = 0, + "18-22" = 1, + "23-27" = 2, + "28-34" = 3, + "35-44" = 4, + "45-59" = 5, + "60-150" = 6 + } + export declare const SpotifyAgeGroupEnum: { + [x: number]: string; + "0-17": (typeof AgeGroups)["0-17"]; + ~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["0-17"]'. + "18-22": (typeof AgeGroups)["18-22"]; + ~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["18-22"]'. + "23-27": (typeof AgeGroups)["23-27"]; + ~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["23-27"]'. + "28-34": (typeof AgeGroups)["28-34"]; + ~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["28-34"]'. + "35-44": (typeof AgeGroups)["35-44"]; + ~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["35-44"]'. + "45-59": (typeof AgeGroups)["45-59"]; + ~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["45-59"]'. + "60-150": (typeof AgeGroups)["60-150"]; + ~~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'AgeGroups' or '(typeof AgeGroups)["60-150"]'. + }; + export {}; + \ No newline at end of file diff --git a/tests/baselines/reference/strictFunctionTypes1.errors.txt b/tests/baselines/reference/strictFunctionTypes1.errors.txt new file mode 100644 index 0000000000000..bd05752448264 --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypes1.errors.txt @@ -0,0 +1,56 @@ +strictFunctionTypes1.ts(15,7): error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"def" | "abc"'. + + +==== strictFunctionTypes1.ts (1 errors) ==== + declare function f1(f1: (x: T) => void, f2: (x: T) => void): (x: T) => void; + declare function f2(obj: T, f1: (x: T) => void, f2: (x: T) => void): T; + declare function f3(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void): T; + + interface Func { (x: T): void } + + declare function f4(f1: Func, f2: Func): Func; + + declare function fo(x: Object): void; + declare function fs(x: string): void; + declare function fx(f: (x: "def") => void): void; + + const x1 = f1(fo, fs); // (x: string) => void + const x2 = f2("abc", fo, fs); // "abc" + const x3 = f3("abc", fo, fx); // "abc" | "def" + ~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"def" | "abc"'. + const x4 = f4(fo, fs); // Func + + declare const never: never; + + const x10 = f2(never, fo, fs); // string + const x11 = f3(never, fo, fx); // "def" + + // Repro from #21112 + + declare function foo(a: ReadonlyArray): T; + let x = foo([]); // never + + // Modified repros from #26127 + + interface A { a: string } + interface B extends A { b: string } + + declare function acceptUnion(x: A | number): void; + declare function acceptA(x: A): void; + + declare let a: A; + declare let b: B; + + declare function coAndContra(value: T, func: (t: T) => void): T; + + const t1: A = coAndContra(a, acceptUnion); + const t2: B = coAndContra(b, acceptA); + const t3: A = coAndContra(never, acceptA); + + declare function coAndContraArray(value: T[], func: (t: T) => void): T[]; + + const t4: A[] = coAndContraArray([a], acceptUnion); + const t5: B[] = coAndContraArray([b], acceptA); + const t6: A[] = coAndContraArray([], acceptA); + \ No newline at end of file diff --git a/tests/baselines/reference/stringLiteralTypesOverloads02.errors.txt b/tests/baselines/reference/stringLiteralTypesOverloads02.errors.txt new file mode 100644 index 0000000000000..f762c457aba1c --- /dev/null +++ b/tests/baselines/reference/stringLiteralTypesOverloads02.errors.txt @@ -0,0 +1,65 @@ +stringLiteralTypesOverloads02.ts(33,7): error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"string" | "number"'. +stringLiteralTypesOverloads02.ts(34,7): error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"string" | "boolean"'. +stringLiteralTypesOverloads02.ts(35,7): error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"number" | "boolean"'. +stringLiteralTypesOverloads02.ts(36,7): error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"string" | "number" | "boolean"'. + + +==== stringLiteralTypesOverloads02.ts (4 errors) ==== + function getFalsyPrimitive(x: "string"): string; + function getFalsyPrimitive(x: "number"): number; + function getFalsyPrimitive(x: "boolean"): boolean; + function getFalsyPrimitive(x: "boolean" | "string"): boolean | string; + function getFalsyPrimitive(x: "boolean" | "number"): boolean | number; + function getFalsyPrimitive(x: "number" | "string"): number | string; + function getFalsyPrimitive(x: "number" | "string" | "boolean"): number | string | boolean; + function getFalsyPrimitive(x: string): string | number | boolean { + if (x === "string") { + return ""; + } + if (x === "number") { + return 0; + } + if (x === "boolean") { + return false; + } + + // Should be unreachable. + throw "Invalid value"; + } + + namespace Consts1 { + const EMPTY_STRING = getFalsyPrimitive("string"); + const ZERO = getFalsyPrimitive('number'); + const FALSE = getFalsyPrimitive("boolean"); + } + + const string = "string" + const number = "number" + const boolean = "boolean" + + const stringOrNumber = string || number; + ~~~~~~~~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"string" | "number"'. + const stringOrBoolean = string || boolean; + ~~~~~~~~~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"string" | "boolean"'. + const booleanOrNumber = number || boolean; + ~~~~~~~~~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"number" | "boolean"'. + const stringOrBooleanOrNumber = stringOrBoolean || number; + ~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2869: The type of this declaration is ambiguous and may be observed as either 'string' or '"string" | "number" | "boolean"'. + + namespace Consts2 { + const EMPTY_STRING = getFalsyPrimitive(string); + const ZERO = getFalsyPrimitive(number); + const FALSE = getFalsyPrimitive(boolean); + + const a = getFalsyPrimitive(stringOrNumber); + const b = getFalsyPrimitive(stringOrBoolean); + const c = getFalsyPrimitive(booleanOrNumber); + const d = getFalsyPrimitive(stringOrBooleanOrNumber); + } + + + \ No newline at end of file diff --git a/tests/cases/compiler/ambiguousLiteralWideningEmit.ts b/tests/cases/compiler/ambiguousLiteralWideningEmit.ts new file mode 100644 index 0000000000000..65aa81d54f50a --- /dev/null +++ b/tests/cases/compiler/ambiguousLiteralWideningEmit.ts @@ -0,0 +1,9 @@ +// @declaration: true +// @strict: true + +declare function pad(n: number | string): string; + +export default (dateString: string, type: 'date' | 'month' | 'year'): string => { + const [year, month = 1, date = 1] = dateString.split('-') + return `${year}-${pad(month)}-${pad(date)}`.substr(0, { date: 10, month: 7, year: 4 }[type]) +} diff --git a/tests/cases/compiler/declarationEmitBindingPatterns2.ts b/tests/cases/compiler/declarationEmitBindingPatterns2.ts new file mode 100644 index 0000000000000..1d43c3639cb0a --- /dev/null +++ b/tests/cases/compiler/declarationEmitBindingPatterns2.ts @@ -0,0 +1,11 @@ +// @strict: true +// @declaration: true +// @emitDeclarationOnly: true + +// https://github.com/microsoft/TypeScript/issues/55439 + +function foo(): { y: 1 } { + return { y: 1 }; +} + +export const { y = 0 } = foo();