diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ac1038a4cdbe..9f13d389a48ed 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28658,14 +28658,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // destructuring from the narrowed parent type. if (isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) { const parent = declaration.parent.parent; - if (parent.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlagsCached(declaration) & NodeFlags.Constant || parent.kind === SyntaxKind.Parameter) { + 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 && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) { + if (parentTypeConstraint && parentTypeConstraint.flags & TypeFlags.Union && !(rootDeclaration.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) { const pattern = declaration.parent; const narrowedType = getFlowTypeOfReference(pattern, parentTypeConstraint, parentTypeConstraint, /*flowContainer*/ undefined, location.flowNode); if (narrowedType.flags & TypeFlags.Never) { diff --git a/tests/baselines/reference/dependentDestructuredVariablesFromNestedPatterns.symbols b/tests/baselines/reference/dependentDestructuredVariablesFromNestedPatterns.symbols new file mode 100644 index 0000000000000..90a5600ec5d14 --- /dev/null +++ b/tests/baselines/reference/dependentDestructuredVariablesFromNestedPatterns.symbols @@ -0,0 +1,138 @@ +//// [tests/cases/conformance/controlFlow/dependentDestructuredVariablesFromNestedPatterns.ts] //// + +=== dependentDestructuredVariablesFromNestedPatterns.ts === +function test1(arg: [[undefined, Error] | [number, undefined]]) { +>test1 : Symbol(test1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 0, 0)) +>arg : Symbol(arg, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 0, 15)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) + + const [[p1, p1Error]] = arg; +>p1 : Symbol(p1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 1, 10)) +>p1Error : Symbol(p1Error, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 1, 13)) +>arg : Symbol(arg, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 0, 15)) + + if (p1Error) { +>p1Error : Symbol(p1Error, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 1, 13)) + + return; + } + + p1; +>p1 : Symbol(p1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 1, 10)) +} + +function test2([[p1, p1Error]]: [[undefined, Error] | [number, undefined]]) { +>test2 : Symbol(test2, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 8, 1)) +>p1 : Symbol(p1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 10, 17)) +>p1Error : Symbol(p1Error, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 10, 20)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) + + if (p1Error) { +>p1Error : Symbol(p1Error, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 10, 20)) + + return; + } + + p1; +>p1 : Symbol(p1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 10, 17)) +} + +async function myAllSettled(fn: () => T) { +>myAllSettled : Symbol(myAllSettled, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 16, 1)) +>T : Symbol(T, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 18, 28)) +>fn : Symbol(fn, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 18, 58)) +>T : Symbol(T, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 18, 28)) + + const promises = await Promise.allSettled(fn()); +>promises : Symbol(promises, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 19, 7)) +>Promise.allSettled : Symbol(PromiseConstructor.allSettled, Decl(lib.es2020.promise.d.ts, --, --), Decl(lib.es2020.promise.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>allSettled : Symbol(PromiseConstructor.allSettled, Decl(lib.es2020.promise.d.ts, --, --), Decl(lib.es2020.promise.d.ts, --, --)) +>fn : Symbol(fn, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 18, 58)) + + return promises.map((result) => +>promises.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>promises : Symbol(promises, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 19, 7)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>result : Symbol(result, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 21, 23)) + + result.status === "fulfilled" +>result.status : Symbol(status, Decl(lib.es2020.promise.d.ts, --, --), Decl(lib.es2020.promise.d.ts, --, --)) +>result : Symbol(result, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 21, 23)) +>status : Symbol(status, Decl(lib.es2020.promise.d.ts, --, --), Decl(lib.es2020.promise.d.ts, --, --)) + + ? [result.value, undefined] +>result.value : Symbol(PromiseFulfilledResult.value, Decl(lib.es2020.promise.d.ts, --, --)) +>result : Symbol(result, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 21, 23)) +>value : Symbol(PromiseFulfilledResult.value, Decl(lib.es2020.promise.d.ts, --, --)) +>undefined : Symbol(undefined) + + : [undefined, new Error(String(result.reason))], +>undefined : Symbol(undefined) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) +>String : Symbol(String, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --) ... and 6 more) +>result.reason : Symbol(PromiseRejectedResult.reason, Decl(lib.es2020.promise.d.ts, --, --)) +>result : Symbol(result, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 21, 23)) +>reason : Symbol(PromiseRejectedResult.reason, Decl(lib.es2020.promise.d.ts, --, --)) + + ) as { [K in keyof T]: [Awaited, undefined] | [undefined, Error] }; +>K : Symbol(K, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 25, 10)) +>T : Symbol(T, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 18, 28)) +>Awaited : Symbol(Awaited, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 18, 28)) +>K : Symbol(K, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 25, 10)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) +} + +async function test3() { +>test3 : Symbol(test3, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 26, 1)) + + const [[p1, p1Error], _] = await myAllSettled( +>p1 : Symbol(p1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 29, 10)) +>p1Error : Symbol(p1Error, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 29, 13)) +>_ : Symbol(_, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 29, 23)) +>myAllSettled : Symbol(myAllSettled, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 16, 1)) + + () => [Promise.resolve(0), Promise.reject(1)] as const, +>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>Promise.reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>reject : Symbol(PromiseConstructor.reject, Decl(lib.es2015.promise.d.ts, --, --)) +>const : Symbol(const) + + ); + + if (p1Error) return; +>p1Error : Symbol(p1Error, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 29, 13)) + + p1; +>p1 : Symbol(p1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 29, 10)) +} + +function test4([[p1, p1Error]]: [[undefined, Error] | [number, undefined]]) { +>test4 : Symbol(test4, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 36, 1)) +>p1 : Symbol(p1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 38, 17)) +>p1Error : Symbol(p1Error, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 38, 20)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + p1 = undefined; +>p1 : Symbol(p1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 38, 17)) +>undefined : Symbol(undefined) + } + if (p1Error) { +>p1Error : Symbol(p1Error, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 38, 20)) + + return; + } + + p1; +>p1 : Symbol(p1, Decl(dependentDestructuredVariablesFromNestedPatterns.ts, 38, 17)) +} + diff --git a/tests/baselines/reference/dependentDestructuredVariablesFromNestedPatterns.types b/tests/baselines/reference/dependentDestructuredVariablesFromNestedPatterns.types new file mode 100644 index 0000000000000..076c4930742ca --- /dev/null +++ b/tests/baselines/reference/dependentDestructuredVariablesFromNestedPatterns.types @@ -0,0 +1,150 @@ +//// [tests/cases/conformance/controlFlow/dependentDestructuredVariablesFromNestedPatterns.ts] //// + +=== dependentDestructuredVariablesFromNestedPatterns.ts === +function test1(arg: [[undefined, Error] | [number, undefined]]) { +>test1 : (arg: [[undefined, Error] | [number, undefined]]) => void +>arg : [[undefined, Error] | [number, undefined]] + + const [[p1, p1Error]] = arg; +>p1 : number | undefined +>p1Error : Error | undefined +>arg : [[undefined, Error] | [number, undefined]] + + if (p1Error) { +>p1Error : Error | undefined + + return; + } + + p1; +>p1 : number +} + +function test2([[p1, p1Error]]: [[undefined, Error] | [number, undefined]]) { +>test2 : ([[p1, p1Error]]: [[undefined, Error] | [number, undefined]]) => void +>p1 : number | undefined +>p1Error : Error | undefined + + if (p1Error) { +>p1Error : Error | undefined + + return; + } + + p1; +>p1 : number +} + +async function myAllSettled(fn: () => T) { +>myAllSettled : (fn: () => T) => Promise<{ [K in keyof T]: [undefined, Error] | [Awaited, undefined]; }> +>fn : () => T + + const promises = await Promise.allSettled(fn()); +>promises : { -readonly [P in keyof T]: PromiseSettledResult>; } +>await Promise.allSettled(fn()) : { -readonly [P in keyof T]: PromiseSettledResult>; } +>Promise.allSettled(fn()) : Promise<{ -readonly [P in keyof T]: PromiseSettledResult>; }> +>Promise.allSettled : { (values: T): Promise<{ -readonly [P in keyof T]: PromiseSettledResult>; }>; (values: Iterable>): Promise>[]>; } +>Promise : PromiseConstructor +>allSettled : { (values: T): Promise<{ -readonly [P in keyof T]: PromiseSettledResult>; }>; (values: Iterable>): Promise>[]>; } +>fn() : T +>fn : () => T + + return promises.map((result) => +>promises.map((result) => result.status === "fulfilled" ? [result.value, undefined] : [undefined, new Error(String(result.reason))], ) as { [K in keyof T]: [Awaited, undefined] | [undefined, Error] } : { [K in keyof T]: [undefined, Error] | [Awaited, undefined]; } +>promises.map((result) => result.status === "fulfilled" ? [result.value, undefined] : [undefined, new Error(String(result.reason))], ) : ([undefined, Error] | [unknown, undefined])[] +>promises.map : (callbackfn: (value: PromiseSettledResult, index: number, array: PromiseSettledResult[]) => U, thisArg?: any) => U[] +>promises : { -readonly [P in keyof T]: PromiseSettledResult>; } +>map : (callbackfn: (value: PromiseSettledResult, index: number, array: PromiseSettledResult[]) => U, thisArg?: any) => U[] +>(result) => result.status === "fulfilled" ? [result.value, undefined] : [undefined, new Error(String(result.reason))] : (result: PromiseSettledResult) => [undefined, Error] | [unknown, undefined] +>result : PromiseSettledResult + + result.status === "fulfilled" +>result.status === "fulfilled" ? [result.value, undefined] : [undefined, new Error(String(result.reason))] : [unknown, undefined] | [undefined, Error] +>result.status === "fulfilled" : boolean +>result.status : "rejected" | "fulfilled" +>result : PromiseSettledResult +>status : "rejected" | "fulfilled" +>"fulfilled" : "fulfilled" + + ? [result.value, undefined] +>[result.value, undefined] : [unknown, undefined] +>result.value : unknown +>result : PromiseFulfilledResult +>value : unknown +>undefined : undefined + + : [undefined, new Error(String(result.reason))], +>[undefined, new Error(String(result.reason))] : [undefined, Error] +>undefined : undefined +>new Error(String(result.reason)) : Error +>Error : ErrorConstructor +>String(result.reason) : string +>String : StringConstructor +>result.reason : any +>result : PromiseRejectedResult +>reason : any + + ) as { [K in keyof T]: [Awaited, undefined] | [undefined, Error] }; +} + +async function test3() { +>test3 : () => Promise + + const [[p1, p1Error], _] = await myAllSettled( +>p1 : number | undefined +>p1Error : Error | undefined +>_ : [undefined, Error] | [never, undefined] +>await myAllSettled( () => [Promise.resolve(0), Promise.reject(1)] as const, ) : [[undefined, Error] | [number, undefined], [undefined, Error] | [never, undefined]] +>myAllSettled( () => [Promise.resolve(0), Promise.reject(1)] as const, ) : Promise<[[undefined, Error] | [number, undefined], [undefined, Error] | [never, undefined]]> +>myAllSettled : (fn: () => T) => Promise<{ [K in keyof T]: [undefined, Error] | [Awaited, undefined]; }> + + () => [Promise.resolve(0), Promise.reject(1)] as const, +>() => [Promise.resolve(0), Promise.reject(1)] as const : () => [Promise, Promise] +>[Promise.resolve(0), Promise.reject(1)] as const : [Promise, Promise] +>[Promise.resolve(0), Promise.reject(1)] : [Promise, Promise] +>Promise.resolve(0) : Promise +>Promise.resolve : { (): Promise; (value: T): Promise>; (value: T | PromiseLike): Promise>; } +>Promise : PromiseConstructor +>resolve : { (): Promise; (value: T): Promise>; (value: T | PromiseLike): Promise>; } +>0 : 0 +>Promise.reject(1) : Promise +>Promise.reject : (reason?: any) => Promise +>Promise : PromiseConstructor +>reject : (reason?: any) => Promise +>1 : 1 + + ); + + if (p1Error) return; +>p1Error : Error | undefined + + p1; +>p1 : number +} + +function test4([[p1, p1Error]]: [[undefined, Error] | [number, undefined]]) { +>test4 : ([[p1, p1Error]]: [[undefined, Error] | [number, undefined]]) => void +>p1 : number | undefined +>p1Error : Error | undefined + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + p1 = undefined; +>p1 = undefined : undefined +>p1 : number | undefined +>undefined : undefined + } + if (p1Error) { +>p1Error : Error | undefined + + return; + } + + p1; +>p1 : number | undefined +} + diff --git a/tests/cases/conformance/controlFlow/dependentDestructuredVariablesFromNestedPatterns.ts b/tests/cases/conformance/controlFlow/dependentDestructuredVariablesFromNestedPatterns.ts new file mode 100644 index 0000000000000..8d92be327b5b1 --- /dev/null +++ b/tests/cases/conformance/controlFlow/dependentDestructuredVariablesFromNestedPatterns.ts @@ -0,0 +1,53 @@ +// @strict: true +// @target: esnext +// @lib: esnext +// @noEmit: true + +function test1(arg: [[undefined, Error] | [number, undefined]]) { + const [[p1, p1Error]] = arg; + + if (p1Error) { + return; + } + + p1; +} + +function test2([[p1, p1Error]]: [[undefined, Error] | [number, undefined]]) { + if (p1Error) { + return; + } + + p1; +} + +async function myAllSettled(fn: () => T) { + const promises = await Promise.allSettled(fn()); + + return promises.map((result) => + result.status === "fulfilled" + ? [result.value, undefined] + : [undefined, new Error(String(result.reason))], + ) as { [K in keyof T]: [Awaited, undefined] | [undefined, Error] }; +} + +async function test3() { + const [[p1, p1Error], _] = await myAllSettled( + () => [Promise.resolve(0), Promise.reject(1)] as const, + ); + + if (p1Error) return; + + p1; +} + +function test4([[p1, p1Error]]: [[undefined, Error] | [number, undefined]]) { + if (Math.random()) { + p1 = undefined; + } + if (p1Error) { + return; + } + + p1; +}