diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 69933033d3929..2f7cf5328638a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27160,6 +27160,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ElementAccessExpression: // The resolvedSymbol property is initialized by checkPropertyAccess or checkElementAccess before we get here. return isConstantReference((node as AccessExpression).expression) && isReadonlySymbol(getNodeLinks(node).resolvedSymbol || unknownSymbol); + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: + const rootDeclaration = getRootDeclaration(node.parent); + return isVariableDeclaration(rootDeclaration) && isVarConstLike(rootDeclaration); } return false; } diff --git a/tests/baselines/reference/controlFlowAliasedDiscriminants.errors.txt b/tests/baselines/reference/controlFlowAliasedDiscriminants.errors.txt new file mode 100644 index 0000000000000..81779d897385d --- /dev/null +++ b/tests/baselines/reference/controlFlowAliasedDiscriminants.errors.txt @@ -0,0 +1,129 @@ +controlFlowAliasedDiscriminants.ts(39,9): error TS18048: 'data1' is possibly 'undefined'. +controlFlowAliasedDiscriminants.ts(40,9): error TS18048: 'data2' is possibly 'undefined'. +controlFlowAliasedDiscriminants.ts(65,9): error TS18048: 'bar2' is possibly 'undefined'. +controlFlowAliasedDiscriminants.ts(66,9): error TS18048: 'bar3' is possibly 'undefined'. +controlFlowAliasedDiscriminants.ts(86,14): error TS1360: Type 'string | number' does not satisfy the expected type 'string'. + Type 'number' is not assignable to type 'string'. +controlFlowAliasedDiscriminants.ts(98,19): error TS1360: Type 'string | number' does not satisfy the expected type 'string'. + Type 'number' is not assignable to type 'string'. + + +==== controlFlowAliasedDiscriminants.ts (6 errors) ==== + type UseQueryResult = { + isSuccess: false; + data: undefined; + } | { + isSuccess: true; + data: T + }; + + function useQuery(): UseQueryResult { + return { + isSuccess: false, + data: undefined, + }; + } + + const { data: data1, isSuccess: isSuccess1 } = useQuery(); + const { data: data2, isSuccess: isSuccess2 } = useQuery(); + const { data: data3, isSuccess: isSuccess3 } = useQuery(); + + if (isSuccess1 && isSuccess2 && isSuccess3) { + data1.toExponential(); // should ok + data2.toExponential(); // should ok + data3.toExponential(); // should ok + } + + const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; + if (areSuccess) { + data1.toExponential(); // should ok + data2.toExponential(); // should ok + data3.toExponential(); // should ok + } + + { + let { data: data1, isSuccess: isSuccess1 } = useQuery(); + let { data: data2, isSuccess: isSuccess2 } = useQuery(); + const { data: data3, isSuccess: isSuccess3 } = useQuery(); + const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; + if (areSuccess) { + data1.toExponential(); // should error + ~~~~~ +!!! error TS18048: 'data1' is possibly 'undefined'. + data2.toExponential(); // should error + ~~~~~ +!!! error TS18048: 'data2' is possibly 'undefined'. + data3.toExponential(); // should ok + } + } + + declare function getArrayResult(): [true, number] | [false, undefined]; + { + const [foo1, bar1] = getArrayResult(); + const [foo2, bar2] = getArrayResult(); + const [foo3, bar3] = getArrayResult(); + const arrayAllSuccess = foo1 && foo2 && foo3; + if (arrayAllSuccess) { + bar1.toExponential(); // should ok + bar2.toExponential(); // should ok + bar3.toExponential(); // should ok + } + } + + { + const [foo1, bar1] = getArrayResult(); + let [foo2, bar2] = getArrayResult(); + let [foo3, bar3] = getArrayResult(); + const arrayAllSuccess = foo1 && foo2 && foo3; + if (arrayAllSuccess) { + bar1.toExponential(); // should ok + bar2.toExponential(); // should error + ~~~~ +!!! error TS18048: 'bar2' is possibly 'undefined'. + bar3.toExponential(); // should error + ~~~~ +!!! error TS18048: 'bar3' is possibly 'undefined'. + } + } + + type Nested = { + type: 'string'; + resp: { + data: string + } + } | { + type: 'number'; + resp: { + data: number; + } + } + + { + let resp!: Nested; + const { resp: { data }, type } = resp; + if (type === 'string') { + data satisfies string; + ~~~~~~~~~ +!!! error TS1360: Type 'string | number' does not satisfy the expected type 'string'. +!!! error TS1360: Type 'number' is not assignable to type 'string'. + } + if (resp.type === 'string') { + resp.resp.data satisfies string; + } + } + + { + + let resp!: Nested; + const { resp: { data: dataAlias }, type } = resp; + if (type === 'string') { + dataAlias satisfies string; + ~~~~~~~~~ +!!! error TS1360: Type 'string | number' does not satisfy the expected type 'string'. +!!! error TS1360: Type 'number' is not assignable to type 'string'. + } + if (resp.type === 'string') { + resp.resp.data satisfies string; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowAliasedDiscriminants.js b/tests/baselines/reference/controlFlowAliasedDiscriminants.js new file mode 100644 index 0000000000000..0be7340f29f63 --- /dev/null +++ b/tests/baselines/reference/controlFlowAliasedDiscriminants.js @@ -0,0 +1,182 @@ +//// [tests/cases/compiler/controlFlowAliasedDiscriminants.ts] //// + +//// [controlFlowAliasedDiscriminants.ts] +type UseQueryResult = { + isSuccess: false; + data: undefined; +} | { + isSuccess: true; + data: T +}; + +function useQuery(): UseQueryResult { + return { + isSuccess: false, + data: undefined, + }; +} + +const { data: data1, isSuccess: isSuccess1 } = useQuery(); +const { data: data2, isSuccess: isSuccess2 } = useQuery(); +const { data: data3, isSuccess: isSuccess3 } = useQuery(); + +if (isSuccess1 && isSuccess2 && isSuccess3) { + data1.toExponential(); // should ok + data2.toExponential(); // should ok + data3.toExponential(); // should ok +} + +const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; +if (areSuccess) { + data1.toExponential(); // should ok + data2.toExponential(); // should ok + data3.toExponential(); // should ok +} + +{ + let { data: data1, isSuccess: isSuccess1 } = useQuery(); + let { data: data2, isSuccess: isSuccess2 } = useQuery(); + const { data: data3, isSuccess: isSuccess3 } = useQuery(); + const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; + if (areSuccess) { + data1.toExponential(); // should error + data2.toExponential(); // should error + data3.toExponential(); // should ok + } +} + +declare function getArrayResult(): [true, number] | [false, undefined]; +{ + const [foo1, bar1] = getArrayResult(); + const [foo2, bar2] = getArrayResult(); + const [foo3, bar3] = getArrayResult(); + const arrayAllSuccess = foo1 && foo2 && foo3; + if (arrayAllSuccess) { + bar1.toExponential(); // should ok + bar2.toExponential(); // should ok + bar3.toExponential(); // should ok + } +} + +{ + const [foo1, bar1] = getArrayResult(); + let [foo2, bar2] = getArrayResult(); + let [foo3, bar3] = getArrayResult(); + const arrayAllSuccess = foo1 && foo2 && foo3; + if (arrayAllSuccess) { + bar1.toExponential(); // should ok + bar2.toExponential(); // should error + bar3.toExponential(); // should error + } +} + +type Nested = { + type: 'string'; + resp: { + data: string + } +} | { + type: 'number'; + resp: { + data: number; + } +} + +{ + let resp!: Nested; + const { resp: { data }, type } = resp; + if (type === 'string') { + data satisfies string; + } + if (resp.type === 'string') { + resp.resp.data satisfies string; + } +} + +{ + + let resp!: Nested; + const { resp: { data: dataAlias }, type } = resp; + if (type === 'string') { + dataAlias satisfies string; + } + if (resp.type === 'string') { + resp.resp.data satisfies string; + } +} + + +//// [controlFlowAliasedDiscriminants.js] +function useQuery() { + return { + isSuccess: false, + data: undefined, + }; +} +var _a = useQuery(), data1 = _a.data, isSuccess1 = _a.isSuccess; +var _b = useQuery(), data2 = _b.data, isSuccess2 = _b.isSuccess; +var _c = useQuery(), data3 = _c.data, isSuccess3 = _c.isSuccess; +if (isSuccess1 && isSuccess2 && isSuccess3) { + data1.toExponential(); // should ok + data2.toExponential(); // should ok + data3.toExponential(); // should ok +} +var areSuccess = isSuccess1 && isSuccess2 && isSuccess3; +if (areSuccess) { + data1.toExponential(); // should ok + data2.toExponential(); // should ok + data3.toExponential(); // should ok +} +{ + var _d = useQuery(), data1_1 = _d.data, isSuccess1_1 = _d.isSuccess; + var _e = useQuery(), data2_1 = _e.data, isSuccess2_1 = _e.isSuccess; + var _f = useQuery(), data3_1 = _f.data, isSuccess3_1 = _f.isSuccess; + var areSuccess_1 = isSuccess1_1 && isSuccess2_1 && isSuccess3_1; + if (areSuccess_1) { + data1_1.toExponential(); // should error + data2_1.toExponential(); // should error + data3_1.toExponential(); // should ok + } +} +{ + var _g = getArrayResult(), foo1 = _g[0], bar1 = _g[1]; + var _h = getArrayResult(), foo2 = _h[0], bar2 = _h[1]; + var _j = getArrayResult(), foo3 = _j[0], bar3 = _j[1]; + var arrayAllSuccess = foo1 && foo2 && foo3; + if (arrayAllSuccess) { + bar1.toExponential(); // should ok + bar2.toExponential(); // should ok + bar3.toExponential(); // should ok + } +} +{ + var _k = getArrayResult(), foo1 = _k[0], bar1 = _k[1]; + var _l = getArrayResult(), foo2 = _l[0], bar2 = _l[1]; + var _m = getArrayResult(), foo3 = _m[0], bar3 = _m[1]; + var arrayAllSuccess = foo1 && foo2 && foo3; + if (arrayAllSuccess) { + bar1.toExponential(); // should ok + bar2.toExponential(); // should error + bar3.toExponential(); // should error + } +} +{ + var resp = void 0; + var data = resp.resp.data, type = resp.type; + if (type === 'string') { + data; + } + if (resp.type === 'string') { + resp.resp.data; + } +} +{ + var resp = void 0; + var dataAlias = resp.resp.data, type = resp.type; + if (type === 'string') { + dataAlias; + } + if (resp.type === 'string') { + resp.resp.data; + } +} diff --git a/tests/baselines/reference/controlFlowAliasedDiscriminants.symbols b/tests/baselines/reference/controlFlowAliasedDiscriminants.symbols new file mode 100644 index 0000000000000..971b4be1c5c73 --- /dev/null +++ b/tests/baselines/reference/controlFlowAliasedDiscriminants.symbols @@ -0,0 +1,327 @@ +//// [tests/cases/compiler/controlFlowAliasedDiscriminants.ts] //// + +=== controlFlowAliasedDiscriminants.ts === +type UseQueryResult = { +>UseQueryResult : Symbol(UseQueryResult, Decl(controlFlowAliasedDiscriminants.ts, 0, 0)) +>T : Symbol(T, Decl(controlFlowAliasedDiscriminants.ts, 0, 20)) + + isSuccess: false; +>isSuccess : Symbol(isSuccess, Decl(controlFlowAliasedDiscriminants.ts, 0, 26)) + + data: undefined; +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 1, 21)) + +} | { + isSuccess: true; +>isSuccess : Symbol(isSuccess, Decl(controlFlowAliasedDiscriminants.ts, 3, 5)) + + data: T +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 4, 20)) +>T : Symbol(T, Decl(controlFlowAliasedDiscriminants.ts, 0, 20)) + +}; + +function useQuery(): UseQueryResult { +>useQuery : Symbol(useQuery, Decl(controlFlowAliasedDiscriminants.ts, 6, 2)) +>UseQueryResult : Symbol(UseQueryResult, Decl(controlFlowAliasedDiscriminants.ts, 0, 0)) + + return { + isSuccess: false, +>isSuccess : Symbol(isSuccess, Decl(controlFlowAliasedDiscriminants.ts, 9, 12)) + + data: undefined, +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 10, 25)) +>undefined : Symbol(undefined) + + }; +} + +const { data: data1, isSuccess: isSuccess1 } = useQuery(); +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 1, 21), Decl(controlFlowAliasedDiscriminants.ts, 4, 20)) +>data1 : Symbol(data1, Decl(controlFlowAliasedDiscriminants.ts, 15, 7)) +>isSuccess : Symbol(isSuccess, Decl(controlFlowAliasedDiscriminants.ts, 0, 26), Decl(controlFlowAliasedDiscriminants.ts, 3, 5)) +>isSuccess1 : Symbol(isSuccess1, Decl(controlFlowAliasedDiscriminants.ts, 15, 20)) +>useQuery : Symbol(useQuery, Decl(controlFlowAliasedDiscriminants.ts, 6, 2)) + +const { data: data2, isSuccess: isSuccess2 } = useQuery(); +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 1, 21), Decl(controlFlowAliasedDiscriminants.ts, 4, 20)) +>data2 : Symbol(data2, Decl(controlFlowAliasedDiscriminants.ts, 16, 7)) +>isSuccess : Symbol(isSuccess, Decl(controlFlowAliasedDiscriminants.ts, 0, 26), Decl(controlFlowAliasedDiscriminants.ts, 3, 5)) +>isSuccess2 : Symbol(isSuccess2, Decl(controlFlowAliasedDiscriminants.ts, 16, 20)) +>useQuery : Symbol(useQuery, Decl(controlFlowAliasedDiscriminants.ts, 6, 2)) + +const { data: data3, isSuccess: isSuccess3 } = useQuery(); +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 1, 21), Decl(controlFlowAliasedDiscriminants.ts, 4, 20)) +>data3 : Symbol(data3, Decl(controlFlowAliasedDiscriminants.ts, 17, 7)) +>isSuccess : Symbol(isSuccess, Decl(controlFlowAliasedDiscriminants.ts, 0, 26), Decl(controlFlowAliasedDiscriminants.ts, 3, 5)) +>isSuccess3 : Symbol(isSuccess3, Decl(controlFlowAliasedDiscriminants.ts, 17, 20)) +>useQuery : Symbol(useQuery, Decl(controlFlowAliasedDiscriminants.ts, 6, 2)) + +if (isSuccess1 && isSuccess2 && isSuccess3) { +>isSuccess1 : Symbol(isSuccess1, Decl(controlFlowAliasedDiscriminants.ts, 15, 20)) +>isSuccess2 : Symbol(isSuccess2, Decl(controlFlowAliasedDiscriminants.ts, 16, 20)) +>isSuccess3 : Symbol(isSuccess3, Decl(controlFlowAliasedDiscriminants.ts, 17, 20)) + + data1.toExponential(); // should ok +>data1.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>data1 : Symbol(data1, Decl(controlFlowAliasedDiscriminants.ts, 15, 7)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + data2.toExponential(); // should ok +>data2.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>data2 : Symbol(data2, Decl(controlFlowAliasedDiscriminants.ts, 16, 7)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + data3.toExponential(); // should ok +>data3.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>data3 : Symbol(data3, Decl(controlFlowAliasedDiscriminants.ts, 17, 7)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +} + +const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; +>areSuccess : Symbol(areSuccess, Decl(controlFlowAliasedDiscriminants.ts, 25, 5)) +>isSuccess1 : Symbol(isSuccess1, Decl(controlFlowAliasedDiscriminants.ts, 15, 20)) +>isSuccess2 : Symbol(isSuccess2, Decl(controlFlowAliasedDiscriminants.ts, 16, 20)) +>isSuccess3 : Symbol(isSuccess3, Decl(controlFlowAliasedDiscriminants.ts, 17, 20)) + +if (areSuccess) { +>areSuccess : Symbol(areSuccess, Decl(controlFlowAliasedDiscriminants.ts, 25, 5)) + + data1.toExponential(); // should ok +>data1.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>data1 : Symbol(data1, Decl(controlFlowAliasedDiscriminants.ts, 15, 7)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + data2.toExponential(); // should ok +>data2.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>data2 : Symbol(data2, Decl(controlFlowAliasedDiscriminants.ts, 16, 7)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + data3.toExponential(); // should ok +>data3.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>data3 : Symbol(data3, Decl(controlFlowAliasedDiscriminants.ts, 17, 7)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +} + +{ + let { data: data1, isSuccess: isSuccess1 } = useQuery(); +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 1, 21), Decl(controlFlowAliasedDiscriminants.ts, 4, 20)) +>data1 : Symbol(data1, Decl(controlFlowAliasedDiscriminants.ts, 33, 9)) +>isSuccess : Symbol(isSuccess, Decl(controlFlowAliasedDiscriminants.ts, 0, 26), Decl(controlFlowAliasedDiscriminants.ts, 3, 5)) +>isSuccess1 : Symbol(isSuccess1, Decl(controlFlowAliasedDiscriminants.ts, 33, 22)) +>useQuery : Symbol(useQuery, Decl(controlFlowAliasedDiscriminants.ts, 6, 2)) + + let { data: data2, isSuccess: isSuccess2 } = useQuery(); +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 1, 21), Decl(controlFlowAliasedDiscriminants.ts, 4, 20)) +>data2 : Symbol(data2, Decl(controlFlowAliasedDiscriminants.ts, 34, 9)) +>isSuccess : Symbol(isSuccess, Decl(controlFlowAliasedDiscriminants.ts, 0, 26), Decl(controlFlowAliasedDiscriminants.ts, 3, 5)) +>isSuccess2 : Symbol(isSuccess2, Decl(controlFlowAliasedDiscriminants.ts, 34, 22)) +>useQuery : Symbol(useQuery, Decl(controlFlowAliasedDiscriminants.ts, 6, 2)) + + const { data: data3, isSuccess: isSuccess3 } = useQuery(); +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 1, 21), Decl(controlFlowAliasedDiscriminants.ts, 4, 20)) +>data3 : Symbol(data3, Decl(controlFlowAliasedDiscriminants.ts, 35, 11)) +>isSuccess : Symbol(isSuccess, Decl(controlFlowAliasedDiscriminants.ts, 0, 26), Decl(controlFlowAliasedDiscriminants.ts, 3, 5)) +>isSuccess3 : Symbol(isSuccess3, Decl(controlFlowAliasedDiscriminants.ts, 35, 24)) +>useQuery : Symbol(useQuery, Decl(controlFlowAliasedDiscriminants.ts, 6, 2)) + + const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; +>areSuccess : Symbol(areSuccess, Decl(controlFlowAliasedDiscriminants.ts, 36, 9)) +>isSuccess1 : Symbol(isSuccess1, Decl(controlFlowAliasedDiscriminants.ts, 33, 22)) +>isSuccess2 : Symbol(isSuccess2, Decl(controlFlowAliasedDiscriminants.ts, 34, 22)) +>isSuccess3 : Symbol(isSuccess3, Decl(controlFlowAliasedDiscriminants.ts, 35, 24)) + + if (areSuccess) { +>areSuccess : Symbol(areSuccess, Decl(controlFlowAliasedDiscriminants.ts, 36, 9)) + + data1.toExponential(); // should error +>data1.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>data1 : Symbol(data1, Decl(controlFlowAliasedDiscriminants.ts, 33, 9)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + data2.toExponential(); // should error +>data2.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>data2 : Symbol(data2, Decl(controlFlowAliasedDiscriminants.ts, 34, 9)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + data3.toExponential(); // should ok +>data3.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>data3 : Symbol(data3, Decl(controlFlowAliasedDiscriminants.ts, 35, 11)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + } +} + +declare function getArrayResult(): [true, number] | [false, undefined]; +>getArrayResult : Symbol(getArrayResult, Decl(controlFlowAliasedDiscriminants.ts, 42, 1)) +{ + const [foo1, bar1] = getArrayResult(); +>foo1 : Symbol(foo1, Decl(controlFlowAliasedDiscriminants.ts, 46, 11)) +>bar1 : Symbol(bar1, Decl(controlFlowAliasedDiscriminants.ts, 46, 16)) +>getArrayResult : Symbol(getArrayResult, Decl(controlFlowAliasedDiscriminants.ts, 42, 1)) + + const [foo2, bar2] = getArrayResult(); +>foo2 : Symbol(foo2, Decl(controlFlowAliasedDiscriminants.ts, 47, 11)) +>bar2 : Symbol(bar2, Decl(controlFlowAliasedDiscriminants.ts, 47, 16)) +>getArrayResult : Symbol(getArrayResult, Decl(controlFlowAliasedDiscriminants.ts, 42, 1)) + + const [foo3, bar3] = getArrayResult(); +>foo3 : Symbol(foo3, Decl(controlFlowAliasedDiscriminants.ts, 48, 11)) +>bar3 : Symbol(bar3, Decl(controlFlowAliasedDiscriminants.ts, 48, 16)) +>getArrayResult : Symbol(getArrayResult, Decl(controlFlowAliasedDiscriminants.ts, 42, 1)) + + const arrayAllSuccess = foo1 && foo2 && foo3; +>arrayAllSuccess : Symbol(arrayAllSuccess, Decl(controlFlowAliasedDiscriminants.ts, 49, 9)) +>foo1 : Symbol(foo1, Decl(controlFlowAliasedDiscriminants.ts, 46, 11)) +>foo2 : Symbol(foo2, Decl(controlFlowAliasedDiscriminants.ts, 47, 11)) +>foo3 : Symbol(foo3, Decl(controlFlowAliasedDiscriminants.ts, 48, 11)) + + if (arrayAllSuccess) { +>arrayAllSuccess : Symbol(arrayAllSuccess, Decl(controlFlowAliasedDiscriminants.ts, 49, 9)) + + bar1.toExponential(); // should ok +>bar1.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>bar1 : Symbol(bar1, Decl(controlFlowAliasedDiscriminants.ts, 46, 16)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + bar2.toExponential(); // should ok +>bar2.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>bar2 : Symbol(bar2, Decl(controlFlowAliasedDiscriminants.ts, 47, 16)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + bar3.toExponential(); // should ok +>bar3.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>bar3 : Symbol(bar3, Decl(controlFlowAliasedDiscriminants.ts, 48, 16)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + } +} + +{ + const [foo1, bar1] = getArrayResult(); +>foo1 : Symbol(foo1, Decl(controlFlowAliasedDiscriminants.ts, 58, 11)) +>bar1 : Symbol(bar1, Decl(controlFlowAliasedDiscriminants.ts, 58, 16)) +>getArrayResult : Symbol(getArrayResult, Decl(controlFlowAliasedDiscriminants.ts, 42, 1)) + + let [foo2, bar2] = getArrayResult(); +>foo2 : Symbol(foo2, Decl(controlFlowAliasedDiscriminants.ts, 59, 9)) +>bar2 : Symbol(bar2, Decl(controlFlowAliasedDiscriminants.ts, 59, 14)) +>getArrayResult : Symbol(getArrayResult, Decl(controlFlowAliasedDiscriminants.ts, 42, 1)) + + let [foo3, bar3] = getArrayResult(); +>foo3 : Symbol(foo3, Decl(controlFlowAliasedDiscriminants.ts, 60, 9)) +>bar3 : Symbol(bar3, Decl(controlFlowAliasedDiscriminants.ts, 60, 14)) +>getArrayResult : Symbol(getArrayResult, Decl(controlFlowAliasedDiscriminants.ts, 42, 1)) + + const arrayAllSuccess = foo1 && foo2 && foo3; +>arrayAllSuccess : Symbol(arrayAllSuccess, Decl(controlFlowAliasedDiscriminants.ts, 61, 9)) +>foo1 : Symbol(foo1, Decl(controlFlowAliasedDiscriminants.ts, 58, 11)) +>foo2 : Symbol(foo2, Decl(controlFlowAliasedDiscriminants.ts, 59, 9)) +>foo3 : Symbol(foo3, Decl(controlFlowAliasedDiscriminants.ts, 60, 9)) + + if (arrayAllSuccess) { +>arrayAllSuccess : Symbol(arrayAllSuccess, Decl(controlFlowAliasedDiscriminants.ts, 61, 9)) + + bar1.toExponential(); // should ok +>bar1.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>bar1 : Symbol(bar1, Decl(controlFlowAliasedDiscriminants.ts, 58, 16)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + bar2.toExponential(); // should error +>bar2.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>bar2 : Symbol(bar2, Decl(controlFlowAliasedDiscriminants.ts, 59, 14)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + bar3.toExponential(); // should error +>bar3.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>bar3 : Symbol(bar3, Decl(controlFlowAliasedDiscriminants.ts, 60, 14)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + } +} + +type Nested = { +>Nested : Symbol(Nested, Decl(controlFlowAliasedDiscriminants.ts, 67, 1)) + + type: 'string'; +>type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 69, 15)) + + resp: { +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 70, 19)) + + data: string +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 71, 11)) + } +} | { + type: 'number'; +>type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 74, 5)) + + resp: { +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 75, 19)) + + data: number; +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 76, 11)) + } +} + +{ + let resp!: Nested; +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 82, 7)) +>Nested : Symbol(Nested, Decl(controlFlowAliasedDiscriminants.ts, 67, 1)) + + const { resp: { data }, type } = resp; +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 70, 19), Decl(controlFlowAliasedDiscriminants.ts, 75, 19)) +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 83, 19)) +>type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 83, 27)) +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 82, 7)) + + if (type === 'string') { +>type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 83, 27)) + + data satisfies string; +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 83, 19)) + } + if (resp.type === 'string') { +>resp.type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 69, 15), Decl(controlFlowAliasedDiscriminants.ts, 74, 5)) +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 82, 7)) +>type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 69, 15), Decl(controlFlowAliasedDiscriminants.ts, 74, 5)) + + resp.resp.data satisfies string; +>resp.resp.data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 71, 11)) +>resp.resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 70, 19)) +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 82, 7)) +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 70, 19)) +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 71, 11)) + } +} + +{ + + let resp!: Nested; +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 94, 7)) +>Nested : Symbol(Nested, Decl(controlFlowAliasedDiscriminants.ts, 67, 1)) + + const { resp: { data: dataAlias }, type } = resp; +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 70, 19), Decl(controlFlowAliasedDiscriminants.ts, 75, 19)) +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 71, 11), Decl(controlFlowAliasedDiscriminants.ts, 76, 11)) +>dataAlias : Symbol(dataAlias, Decl(controlFlowAliasedDiscriminants.ts, 95, 19)) +>type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 95, 38)) +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 94, 7)) + + if (type === 'string') { +>type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 95, 38)) + + dataAlias satisfies string; +>dataAlias : Symbol(dataAlias, Decl(controlFlowAliasedDiscriminants.ts, 95, 19)) + } + if (resp.type === 'string') { +>resp.type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 69, 15), Decl(controlFlowAliasedDiscriminants.ts, 74, 5)) +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 94, 7)) +>type : Symbol(type, Decl(controlFlowAliasedDiscriminants.ts, 69, 15), Decl(controlFlowAliasedDiscriminants.ts, 74, 5)) + + resp.resp.data satisfies string; +>resp.resp.data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 71, 11)) +>resp.resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 70, 19)) +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 94, 7)) +>resp : Symbol(resp, Decl(controlFlowAliasedDiscriminants.ts, 70, 19)) +>data : Symbol(data, Decl(controlFlowAliasedDiscriminants.ts, 71, 11)) + } +} + diff --git a/tests/baselines/reference/controlFlowAliasedDiscriminants.types b/tests/baselines/reference/controlFlowAliasedDiscriminants.types new file mode 100644 index 0000000000000..5925a680bcc89 --- /dev/null +++ b/tests/baselines/reference/controlFlowAliasedDiscriminants.types @@ -0,0 +1,378 @@ +//// [tests/cases/compiler/controlFlowAliasedDiscriminants.ts] //// + +=== controlFlowAliasedDiscriminants.ts === +type UseQueryResult = { +>UseQueryResult : UseQueryResult + + isSuccess: false; +>isSuccess : false +>false : false + + data: undefined; +>data : undefined + +} | { + isSuccess: true; +>isSuccess : true +>true : true + + data: T +>data : T + +}; + +function useQuery(): UseQueryResult { +>useQuery : () => UseQueryResult + + return { +>{ isSuccess: false, data: undefined, } : { isSuccess: false; data: undefined; } + + isSuccess: false, +>isSuccess : false +>false : false + + data: undefined, +>data : undefined +>undefined : undefined + + }; +} + +const { data: data1, isSuccess: isSuccess1 } = useQuery(); +>data : any +>data1 : number | undefined +>isSuccess : any +>isSuccess1 : boolean +>useQuery() : UseQueryResult +>useQuery : () => UseQueryResult + +const { data: data2, isSuccess: isSuccess2 } = useQuery(); +>data : any +>data2 : number | undefined +>isSuccess : any +>isSuccess2 : boolean +>useQuery() : UseQueryResult +>useQuery : () => UseQueryResult + +const { data: data3, isSuccess: isSuccess3 } = useQuery(); +>data : any +>data3 : number | undefined +>isSuccess : any +>isSuccess3 : boolean +>useQuery() : UseQueryResult +>useQuery : () => UseQueryResult + +if (isSuccess1 && isSuccess2 && isSuccess3) { +>isSuccess1 && isSuccess2 && isSuccess3 : boolean +>isSuccess1 && isSuccess2 : boolean +>isSuccess1 : boolean +>isSuccess2 : boolean +>isSuccess3 : boolean + + data1.toExponential(); // should ok +>data1.toExponential() : string +>data1.toExponential : (fractionDigits?: number | undefined) => string +>data1 : number +>toExponential : (fractionDigits?: number | undefined) => string + + data2.toExponential(); // should ok +>data2.toExponential() : string +>data2.toExponential : (fractionDigits?: number | undefined) => string +>data2 : number +>toExponential : (fractionDigits?: number | undefined) => string + + data3.toExponential(); // should ok +>data3.toExponential() : string +>data3.toExponential : (fractionDigits?: number | undefined) => string +>data3 : number +>toExponential : (fractionDigits?: number | undefined) => string +} + +const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; +>areSuccess : boolean +>isSuccess1 && isSuccess2 && isSuccess3 : boolean +>isSuccess1 && isSuccess2 : boolean +>isSuccess1 : boolean +>isSuccess2 : boolean +>isSuccess3 : boolean + +if (areSuccess) { +>areSuccess : boolean + + data1.toExponential(); // should ok +>data1.toExponential() : string +>data1.toExponential : (fractionDigits?: number | undefined) => string +>data1 : number +>toExponential : (fractionDigits?: number | undefined) => string + + data2.toExponential(); // should ok +>data2.toExponential() : string +>data2.toExponential : (fractionDigits?: number | undefined) => string +>data2 : number +>toExponential : (fractionDigits?: number | undefined) => string + + data3.toExponential(); // should ok +>data3.toExponential() : string +>data3.toExponential : (fractionDigits?: number | undefined) => string +>data3 : number +>toExponential : (fractionDigits?: number | undefined) => string +} + +{ + let { data: data1, isSuccess: isSuccess1 } = useQuery(); +>data : any +>data1 : number | undefined +>isSuccess : any +>isSuccess1 : boolean +>useQuery() : UseQueryResult +>useQuery : () => UseQueryResult + + let { data: data2, isSuccess: isSuccess2 } = useQuery(); +>data : any +>data2 : number | undefined +>isSuccess : any +>isSuccess2 : boolean +>useQuery() : UseQueryResult +>useQuery : () => UseQueryResult + + const { data: data3, isSuccess: isSuccess3 } = useQuery(); +>data : any +>data3 : number | undefined +>isSuccess : any +>isSuccess3 : boolean +>useQuery() : UseQueryResult +>useQuery : () => UseQueryResult + + const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; +>areSuccess : boolean +>isSuccess1 && isSuccess2 && isSuccess3 : boolean +>isSuccess1 && isSuccess2 : boolean +>isSuccess1 : boolean +>isSuccess2 : boolean +>isSuccess3 : boolean + + if (areSuccess) { +>areSuccess : boolean + + data1.toExponential(); // should error +>data1.toExponential() : string +>data1.toExponential : (fractionDigits?: number | undefined) => string +>data1 : number | undefined +>toExponential : (fractionDigits?: number | undefined) => string + + data2.toExponential(); // should error +>data2.toExponential() : string +>data2.toExponential : (fractionDigits?: number | undefined) => string +>data2 : number | undefined +>toExponential : (fractionDigits?: number | undefined) => string + + data3.toExponential(); // should ok +>data3.toExponential() : string +>data3.toExponential : (fractionDigits?: number | undefined) => string +>data3 : number +>toExponential : (fractionDigits?: number | undefined) => string + } +} + +declare function getArrayResult(): [true, number] | [false, undefined]; +>getArrayResult : () => [true, number] | [false, undefined] +>true : true +>false : false +{ + const [foo1, bar1] = getArrayResult(); +>foo1 : boolean +>bar1 : number | undefined +>getArrayResult() : [true, number] | [false, undefined] +>getArrayResult : () => [true, number] | [false, undefined] + + const [foo2, bar2] = getArrayResult(); +>foo2 : boolean +>bar2 : number | undefined +>getArrayResult() : [true, number] | [false, undefined] +>getArrayResult : () => [true, number] | [false, undefined] + + const [foo3, bar3] = getArrayResult(); +>foo3 : boolean +>bar3 : number | undefined +>getArrayResult() : [true, number] | [false, undefined] +>getArrayResult : () => [true, number] | [false, undefined] + + const arrayAllSuccess = foo1 && foo2 && foo3; +>arrayAllSuccess : boolean +>foo1 && foo2 && foo3 : boolean +>foo1 && foo2 : boolean +>foo1 : boolean +>foo2 : boolean +>foo3 : boolean + + if (arrayAllSuccess) { +>arrayAllSuccess : boolean + + bar1.toExponential(); // should ok +>bar1.toExponential() : string +>bar1.toExponential : (fractionDigits?: number | undefined) => string +>bar1 : number +>toExponential : (fractionDigits?: number | undefined) => string + + bar2.toExponential(); // should ok +>bar2.toExponential() : string +>bar2.toExponential : (fractionDigits?: number | undefined) => string +>bar2 : number +>toExponential : (fractionDigits?: number | undefined) => string + + bar3.toExponential(); // should ok +>bar3.toExponential() : string +>bar3.toExponential : (fractionDigits?: number | undefined) => string +>bar3 : number +>toExponential : (fractionDigits?: number | undefined) => string + } +} + +{ + const [foo1, bar1] = getArrayResult(); +>foo1 : boolean +>bar1 : number | undefined +>getArrayResult() : [true, number] | [false, undefined] +>getArrayResult : () => [true, number] | [false, undefined] + + let [foo2, bar2] = getArrayResult(); +>foo2 : boolean +>bar2 : number | undefined +>getArrayResult() : [true, number] | [false, undefined] +>getArrayResult : () => [true, number] | [false, undefined] + + let [foo3, bar3] = getArrayResult(); +>foo3 : boolean +>bar3 : number | undefined +>getArrayResult() : [true, number] | [false, undefined] +>getArrayResult : () => [true, number] | [false, undefined] + + const arrayAllSuccess = foo1 && foo2 && foo3; +>arrayAllSuccess : boolean +>foo1 && foo2 && foo3 : boolean +>foo1 && foo2 : boolean +>foo1 : boolean +>foo2 : boolean +>foo3 : boolean + + if (arrayAllSuccess) { +>arrayAllSuccess : boolean + + bar1.toExponential(); // should ok +>bar1.toExponential() : string +>bar1.toExponential : (fractionDigits?: number | undefined) => string +>bar1 : number +>toExponential : (fractionDigits?: number | undefined) => string + + bar2.toExponential(); // should error +>bar2.toExponential() : string +>bar2.toExponential : (fractionDigits?: number | undefined) => string +>bar2 : number | undefined +>toExponential : (fractionDigits?: number | undefined) => string + + bar3.toExponential(); // should error +>bar3.toExponential() : string +>bar3.toExponential : (fractionDigits?: number | undefined) => string +>bar3 : number | undefined +>toExponential : (fractionDigits?: number | undefined) => string + } +} + +type Nested = { +>Nested : { type: 'string'; resp: { data: string;}; } | { type: 'number'; resp: { data: number;}; } + + type: 'string'; +>type : "string" + + resp: { +>resp : { data: string; } + + data: string +>data : string + } +} | { + type: 'number'; +>type : "number" + + resp: { +>resp : { data: number; } + + data: number; +>data : number + } +} + +{ + let resp!: Nested; +>resp : Nested + + const { resp: { data }, type } = resp; +>resp : any +>data : string | number +>type : "string" | "number" +>resp : Nested + + if (type === 'string') { +>type === 'string' : boolean +>type : "string" | "number" +>'string' : "string" + + data satisfies string; +>data satisfies string : string | number +>data : string | number + } + if (resp.type === 'string') { +>resp.type === 'string' : boolean +>resp.type : "string" | "number" +>resp : Nested +>type : "string" | "number" +>'string' : "string" + + resp.resp.data satisfies string; +>resp.resp.data satisfies string : string +>resp.resp.data : string +>resp.resp : { data: string; } +>resp : { type: "string"; resp: { data: string; }; } +>resp : { data: string; } +>data : string + } +} + +{ + + let resp!: Nested; +>resp : Nested + + const { resp: { data: dataAlias }, type } = resp; +>resp : any +>data : any +>dataAlias : string | number +>type : "string" | "number" +>resp : Nested + + if (type === 'string') { +>type === 'string' : boolean +>type : "string" | "number" +>'string' : "string" + + dataAlias satisfies string; +>dataAlias satisfies string : string | number +>dataAlias : string | number + } + if (resp.type === 'string') { +>resp.type === 'string' : boolean +>resp.type : "string" | "number" +>resp : Nested +>type : "string" | "number" +>'string' : "string" + + resp.resp.data satisfies string; +>resp.resp.data satisfies string : string +>resp.resp.data : string +>resp.resp : { data: string; } +>resp : { type: "string"; resp: { data: string; }; } +>resp : { data: string; } +>data : string + } +} + diff --git a/tests/cases/compiler/controlFlowAliasedDiscriminants.ts b/tests/cases/compiler/controlFlowAliasedDiscriminants.ts new file mode 100644 index 0000000000000..53e7bdd8bd352 --- /dev/null +++ b/tests/cases/compiler/controlFlowAliasedDiscriminants.ts @@ -0,0 +1,106 @@ +// @strictNullChecks: true +// @noImplicitAny: true + +type UseQueryResult = { + isSuccess: false; + data: undefined; +} | { + isSuccess: true; + data: T +}; + +function useQuery(): UseQueryResult { + return { + isSuccess: false, + data: undefined, + }; +} + +const { data: data1, isSuccess: isSuccess1 } = useQuery(); +const { data: data2, isSuccess: isSuccess2 } = useQuery(); +const { data: data3, isSuccess: isSuccess3 } = useQuery(); + +if (isSuccess1 && isSuccess2 && isSuccess3) { + data1.toExponential(); // should ok + data2.toExponential(); // should ok + data3.toExponential(); // should ok +} + +const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; +if (areSuccess) { + data1.toExponential(); // should ok + data2.toExponential(); // should ok + data3.toExponential(); // should ok +} + +{ + let { data: data1, isSuccess: isSuccess1 } = useQuery(); + let { data: data2, isSuccess: isSuccess2 } = useQuery(); + const { data: data3, isSuccess: isSuccess3 } = useQuery(); + const areSuccess = isSuccess1 && isSuccess2 && isSuccess3; + if (areSuccess) { + data1.toExponential(); // should error + data2.toExponential(); // should error + data3.toExponential(); // should ok + } +} + +declare function getArrayResult(): [true, number] | [false, undefined]; +{ + const [foo1, bar1] = getArrayResult(); + const [foo2, bar2] = getArrayResult(); + const [foo3, bar3] = getArrayResult(); + const arrayAllSuccess = foo1 && foo2 && foo3; + if (arrayAllSuccess) { + bar1.toExponential(); // should ok + bar2.toExponential(); // should ok + bar3.toExponential(); // should ok + } +} + +{ + const [foo1, bar1] = getArrayResult(); + let [foo2, bar2] = getArrayResult(); + let [foo3, bar3] = getArrayResult(); + const arrayAllSuccess = foo1 && foo2 && foo3; + if (arrayAllSuccess) { + bar1.toExponential(); // should ok + bar2.toExponential(); // should error + bar3.toExponential(); // should error + } +} + +type Nested = { + type: 'string'; + resp: { + data: string + } +} | { + type: 'number'; + resp: { + data: number; + } +} + +{ + let resp!: Nested; + const { resp: { data }, type } = resp; + if (type === 'string') { + data satisfies string; + } + if (resp.type === 'string') { + resp.resp.data satisfies string; + } +} + +{ + + let resp!: Nested; + const { resp: { data: dataAlias }, type } = resp; + if (type === 'string') { + dataAlias satisfies string; + } + if (resp.type === 'string') { + resp.resp.data satisfies string; + } +}