diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 390c843b0c968..22e157ab2f0df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30838,7 +30838,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getNarrowedTypeOfSymbol(symbol: Symbol, location: Identifier) { const type = getTypeOfSymbol(symbol); const declaration = symbol.valueDeclaration; - if (declaration) { + if (declaration && !textRangeContainsPositionInclusive(getRootDeclaration(declaration), location.pos)) { // If we have a non-rest binding element with no initializer declared as a const variable or a const-like // parameter (a parameter for which there are no assignments in the function body), and if the parent type // for the destructuring is a union type, one or more of the binding elements may represent discriminant @@ -30866,23 +30866,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const parent = declaration.parent.parent; const rootDeclaration = getRootDeclaration(parent); if (rootDeclaration.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlagsCached(rootDeclaration) & NodeFlags.Constant || rootDeclaration.kind === SyntaxKind.Parameter) { - const links = getNodeLinks(parent); - if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) { - links.flags |= NodeCheckFlags.InCheckIdentifier; - const parentType = getTypeForBindingElementParent(parent, CheckMode.Normal); - const parentTypeConstraint = parentType && mapType(parentType, getBaseConstraintOrType); - links.flags &= ~NodeCheckFlags.InCheckIdentifier; - if (parentTypeConstraint && parentTypeConstraint.flags & TypeFlags.Union && !(rootDeclaration.kind === SyntaxKind.Parameter && isSomeSymbolAssigned(rootDeclaration))) { - const pattern = declaration.parent; - const narrowedType = getFlowTypeOfReference(pattern, parentTypeConstraint, parentTypeConstraint, /*flowContainer*/ undefined, location.flowNode); - if (narrowedType.flags & TypeFlags.Never) { - return neverType; - } - // Destructurings are validated against the parent type elsewhere. Here we disable tuple bounds - // checks because the narrowed type may have lower arity than the full parent type. For example, - // for the declaration [x, y]: [1, 2] | [3], we may have narrowed the parent type to just [3]. - return getBindingElementTypeFromParentType(declaration, narrowedType, /*noTupleBoundsCheck*/ true); + const parentType = getTypeForBindingElementParent(parent, CheckMode.Normal); + const parentTypeConstraint = parentType && mapType(parentType, getBaseConstraintOrType); + if (parentTypeConstraint && parentTypeConstraint.flags & TypeFlags.Union && !(rootDeclaration.kind === SyntaxKind.Parameter && isSomeSymbolAssigned(rootDeclaration))) { + const pattern = declaration.parent; + const narrowedType = getFlowTypeOfReference(pattern, parentTypeConstraint, parentTypeConstraint, /*flowContainer*/ undefined, location.flowNode); + if (narrowedType.flags & TypeFlags.Never) { + return neverType; } + // Destructurings are validated against the parent type elsewhere. Here we disable tuple bounds + // checks because the narrowed type may have lower arity than the full parent type. For example, + // for the declaration [x, y]: [1, 2] | [3], we may have narrowed the parent type to just [3]. + return getBindingElementTypeFromParentType(declaration, narrowedType, /*noTupleBoundsCheck*/ true); } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4e9f872720d79..c089c3a87319b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6240,8 +6240,7 @@ export const enum NodeCheckFlags { ConstructorReference = 1 << 29, // Binding to a class constructor inside of the class's body. ContainsClassWithPrivateIdentifiers = 1 << 20, // Marked on all block-scoped containers containing a class with private identifiers. ContainsSuperPropertyInStaticInitializer = 1 << 21, // Marked on all block-scoped containers containing a static initializer with 'super.x' or 'super[x]'. - InCheckIdentifier = 1 << 22, - PartiallyTypeChecked = 1 << 23, // Node has been partially type checked + PartiallyTypeChecked = 1 << 22, // Node has been partially type checked /** These flags are LazyNodeCheckFlags and can be calculated lazily by `hasNodeCheckFlag` */ LazyFlags = SuperInstance diff --git a/tests/baselines/reference/dependentDestructuredVariablesRefereinceInDeclaration1.errors.txt b/tests/baselines/reference/dependentDestructuredVariablesRefereinceInDeclaration1.errors.txt new file mode 100644 index 0000000000000..dbc8acc6b4c5b --- /dev/null +++ b/tests/baselines/reference/dependentDestructuredVariablesRefereinceInDeclaration1.errors.txt @@ -0,0 +1,93 @@ +dependentDestructuredVariablesRefereinceInDeclaration1.ts(4,9): error TS2322: Type '{ c: number; f: any; }' is not assignable to type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(4,11): error TS2339: Property 'c' does not exist on type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(4,14): error TS2339: Property 'f' does not exist on type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(4,14): error TS7022: 'f' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(4,45): error TS2448: Block-scoped variable 'f' used before its declaration. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(9,9): error TS2322: Type '{ c: number; f: () => any; }' is not assignable to type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(9,11): error TS2339: Property 'c' does not exist on type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(9,14): error TS2339: Property 'f' does not exist on type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(14,9): error TS2322: Type '{ c: number; f: number; g: number; }' is not assignable to type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(14,11): error TS2339: Property 'c' does not exist on type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(14,14): error TS2339: Property 'f' does not exist on type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(14,17): error TS2339: Property 'g' does not exist on type 'string | number'. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(20,14): error TS7022: 'f' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +dependentDestructuredVariablesRefereinceInDeclaration1.ts(20,71): error TS2448: Block-scoped variable 'f' used before its declaration. + + +==== dependentDestructuredVariablesRefereinceInDeclaration1.ts (14 errors) ==== + // https://github.com/microsoft/TypeScript/issues/62993 + + { + const { c, f }: string | number = { c: 0, f }; + ~~~~~~~~ +!!! error TS2322: Type '{ c: number; f: any; }' is not assignable to type 'string | number'. + ~ +!!! error TS2339: Property 'c' does not exist on type 'string | number'. + ~ +!!! error TS2339: Property 'f' does not exist on type 'string | number'. + ~ +!!! error TS7022: 'f' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + ~ +!!! error TS2448: Block-scoped variable 'f' used before its declaration. +!!! related TS2728 dependentDestructuredVariablesRefereinceInDeclaration1.ts:4:14: 'f' is declared here. + f; + } + + { + const { c, f }: string | number = { c: 0, f: () => f }; + ~~~~~~~~ +!!! error TS2322: Type '{ c: number; f: () => any; }' is not assignable to type 'string | number'. + ~ +!!! error TS2339: Property 'c' does not exist on type 'string | number'. + ~ +!!! error TS2339: Property 'f' does not exist on type 'string | number'. + f; + } + + { + const { c, f, g = f }: string | number = { c: 0, f: 0, g: 0 }; + ~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ c: number; f: number; g: number; }' is not assignable to type 'string | number'. + ~ +!!! error TS2339: Property 'c' does not exist on type 'string | number'. + ~ +!!! error TS2339: Property 'f' does not exist on type 'string | number'. + ~ +!!! error TS2339: Property 'g' does not exist on type 'string | number'. + f; + g; + } + + { + const { c, f }: { c: 0; f: number } | { c: 1; f: string } = { c: 0, f }; + ~ +!!! error TS7022: 'f' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + ~ +!!! error TS2448: Block-scoped variable 'f' used before its declaration. +!!! related TS2728 dependentDestructuredVariablesRefereinceInDeclaration1.ts:20:14: 'f' is declared here. + f; + } + + { + const { c, f }: { c: 0; f: () => unknown } | { c: 1; f: string } = { + c: 0, + f: () => f, + }; + f; + } + + { + const { + c, + f, + g = f, + }: + | { c: 0; f: bigint; g?: bigint | number } + | { c: 1; f: number; g: string } = { + c: 0, + f: 10n, + }; + f; + g; + } + \ No newline at end of file diff --git a/tests/baselines/reference/dependentDestructuredVariablesRefereinceInDeclaration1.symbols b/tests/baselines/reference/dependentDestructuredVariablesRefereinceInDeclaration1.symbols new file mode 100644 index 0000000000000..a42f22fdad78d --- /dev/null +++ b/tests/baselines/reference/dependentDestructuredVariablesRefereinceInDeclaration1.symbols @@ -0,0 +1,118 @@ +//// [tests/cases/conformance/controlFlow/dependentDestructuredVariablesRefereinceInDeclaration1.ts] //// + +=== dependentDestructuredVariablesRefereinceInDeclaration1.ts === +// https://github.com/microsoft/TypeScript/issues/62993 + +{ + const { c, f }: string | number = { c: 0, f }; +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 3, 9)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 3, 12)) +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 3, 37)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 3, 43)) + + f; +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 3, 12)) +} + +{ + const { c, f }: string | number = { c: 0, f: () => f }; +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 8, 9)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 8, 12)) +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 8, 37)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 8, 43)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 8, 12)) + + f; +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 8, 12)) +} + +{ + const { c, f, g = f }: string | number = { c: 0, f: 0, g: 0 }; +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 13, 9)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 13, 12)) +>g : Symbol(g, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 13, 15)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 13, 12)) +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 13, 44)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 13, 50)) +>g : Symbol(g, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 13, 56)) + + f; +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 13, 12)) + + g; +>g : Symbol(g, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 13, 15)) +} + +{ + const { c, f }: { c: 0; f: number } | { c: 1; f: string } = { c: 0, f }; +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 19, 9)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 19, 12)) +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 19, 19)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 19, 25)) +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 19, 41)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 19, 47)) +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 19, 63)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 19, 69)) + + f; +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 19, 12)) +} + +{ + const { c, f }: { c: 0; f: () => unknown } | { c: 1; f: string } = { +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 24, 9)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 24, 12)) +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 24, 19)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 24, 25)) +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 24, 48)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 24, 54)) + + c: 0, +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 24, 70)) + + f: () => f, +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 25, 9)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 24, 12)) + + }; + f; +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 24, 12)) +} + +{ + const { + c, +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 32, 9)) + + f, +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 33, 6)) + + g = f, +>g : Symbol(g, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 34, 6)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 33, 6)) + + }: + | { c: 0; f: bigint; g?: bigint | number } +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 37, 7)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 37, 13)) +>g : Symbol(g, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 37, 24)) + + | { c: 1; f: number; g: string } = { +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 38, 7)) +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 38, 13)) +>g : Symbol(g, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 38, 24)) + + c: 0, +>c : Symbol(c, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 38, 40)) + + f: 10n, +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 39, 9)) + + }; + f; +>f : Symbol(f, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 33, 6)) + + g; +>g : Symbol(g, Decl(dependentDestructuredVariablesRefereinceInDeclaration1.ts, 34, 6)) +} + diff --git a/tests/baselines/reference/dependentDestructuredVariablesRefereinceInDeclaration1.types b/tests/baselines/reference/dependentDestructuredVariablesRefereinceInDeclaration1.types new file mode 100644 index 0000000000000..17794489a97d0 --- /dev/null +++ b/tests/baselines/reference/dependentDestructuredVariablesRefereinceInDeclaration1.types @@ -0,0 +1,205 @@ +//// [tests/cases/conformance/controlFlow/dependentDestructuredVariablesRefereinceInDeclaration1.ts] //// + +=== dependentDestructuredVariablesRefereinceInDeclaration1.ts === +// https://github.com/microsoft/TypeScript/issues/62993 + +{ + const { c, f }: string | number = { c: 0, f }; +>c : any +> : ^^^ +>f : any +> : ^^^ +>{ c: 0, f } : { c: number; f: any; } +> : ^^^^^^^^^^^^^^^^^^^^^^ +>c : number +> : ^^^^^^ +>0 : 0 +> : ^ +>f : any +> : ^^^ + + f; +>f : any +> : ^^^ +} + +{ + const { c, f }: string | number = { c: 0, f: () => f }; +>c : any +> : ^^^ +>f : any +> : ^^^ +>{ c: 0, f: () => f } : { c: number; f: () => any; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>c : number +> : ^^^^^^ +>0 : 0 +> : ^ +>f : () => any +> : ^^^^^^^^^ +>() => f : () => any +> : ^^^^^^^^^ +>f : any +> : ^^^ + + f; +>f : any +> : ^^^ +} + +{ + const { c, f, g = f }: string | number = { c: 0, f: 0, g: 0 }; +>c : any +> : ^^^ +>f : any +> : ^^^ +>g : any +> : ^^^ +>f : any +> : ^^^ +>{ c: 0, f: 0, g: 0 } : { c: number; f: number; g: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>c : number +> : ^^^^^^ +>0 : 0 +> : ^ +>f : number +> : ^^^^^^ +>0 : 0 +> : ^ +>g : number +> : ^^^^^^ +>0 : 0 +> : ^ + + f; +>f : any +> : ^^^ + + g; +>g : any +> : ^^^ +} + +{ + const { c, f }: { c: 0; f: number } | { c: 1; f: string } = { c: 0, f }; +>c : 0 | 1 +> : ^^^^^ +>f : any +> : ^^^ +>c : 0 +> : ^ +>f : number +> : ^^^^^^ +>c : 1 +> : ^ +>f : string +> : ^^^^^^ +>{ c: 0, f } : { c: 0; f: any; } +> : ^^^^^^^^^^^^^^^^^ +>c : 0 +> : ^ +>0 : 0 +> : ^ +>f : any +> : ^^^ + + f; +>f : string | number +> : ^^^^^^^^^^^^^^^ +} + +{ + const { c, f }: { c: 0; f: () => unknown } | { c: 1; f: string } = { +>c : 0 | 1 +> : ^^^^^ +>f : string | (() => unknown) +> : ^^^^^^^^^^^^^^^^ ^ +>c : 0 +> : ^ +>f : () => unknown +> : ^^^^^^ +>c : 1 +> : ^ +>f : string +> : ^^^^^^ +>{ c: 0, f: () => f, } : { c: 0; f: () => string | (() => unknown); } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ + + c: 0, +>c : 0 +> : ^ +>0 : 0 +> : ^ + + f: () => f, +>f : () => string | (() => unknown) +> : ^^^^^^^^^^^^^^^^^^^^^^ ^ +>() => f : () => string | (() => unknown) +> : ^^^^^^^^^^^^^^^^^^^^^^ ^ +>f : string | (() => unknown) +> : ^^^^^^^^^^^^^^^^ ^ + + }; + f; +>f : () => unknown +> : ^^^^^^ +} + +{ + const { + c, +>c : 0 | 1 +> : ^^^^^ + + f, +>f : number | bigint +> : ^^^^^^^^^^^^^^^ + + g = f, +>g : string | number | bigint +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +>f : number | bigint +> : ^^^^^^^^^^^^^^^ + + }: + | { c: 0; f: bigint; g?: bigint | number } +>c : 0 +> : ^ +>f : bigint +> : ^^^^^^ +>g : number | bigint | undefined +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + | { c: 1; f: number; g: string } = { +>c : 1 +> : ^ +>f : number +> : ^^^^^^ +>g : string +> : ^^^^^^ +>{ c: 0, f: 10n, } : { c: 0; f: bigint; } +> : ^^^^^^^^^^^^^^^^^^^^ + + c: 0, +>c : 0 +> : ^ +>0 : 0 +> : ^ + + f: 10n, +>f : bigint +> : ^^^^^^ +>10n : 10n +> : ^^^ + + }; + f; +>f : bigint +> : ^^^^^^ + + g; +>g : string | number | bigint +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +} + diff --git a/tests/cases/conformance/controlFlow/dependentDestructuredVariablesRefereinceInDeclaration1.ts b/tests/cases/conformance/controlFlow/dependentDestructuredVariablesRefereinceInDeclaration1.ts new file mode 100644 index 0000000000000..0e62c895292d3 --- /dev/null +++ b/tests/cases/conformance/controlFlow/dependentDestructuredVariablesRefereinceInDeclaration1.ts @@ -0,0 +1,49 @@ +// @strict: true +// @target: esnext +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/62993 + +{ + const { c, f }: string | number = { c: 0, f }; + f; +} + +{ + const { c, f }: string | number = { c: 0, f: () => f }; + f; +} + +{ + const { c, f, g = f }: string | number = { c: 0, f: 0, g: 0 }; + f; + g; +} + +{ + const { c, f }: { c: 0; f: number } | { c: 1; f: string } = { c: 0, f }; + f; +} + +{ + const { c, f }: { c: 0; f: () => unknown } | { c: 1; f: string } = { + c: 0, + f: () => f, + }; + f; +} + +{ + const { + c, + f, + g = f, + }: + | { c: 0; f: bigint; g?: bigint | number } + | { c: 1; f: number; g: string } = { + c: 0, + f: 10n, + }; + f; + g; +}