From 27359b016de8031f6a5777282a03aec7cc324174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 3 Mar 2022 22:30:15 +0100 Subject: [PATCH 1/2] Add failing test for dependent destructured variables within obj methods --- .../dependentDestructuredVariables.errors.txt | 324 ++++++++++++++++++ .../dependentDestructuredVariables.js | 175 ++++++++++ .../dependentDestructuredVariables.symbols | 163 ++++++++- .../dependentDestructuredVariables.types | 178 ++++++++++ .../dependentDestructuredVariables.ts | 71 ++++ 5 files changed, 908 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/dependentDestructuredVariables.errors.txt diff --git a/tests/baselines/reference/dependentDestructuredVariables.errors.txt b/tests/baselines/reference/dependentDestructuredVariables.errors.txt new file mode 100644 index 0000000000000..5df33eb17fb47 --- /dev/null +++ b/tests/baselines/reference/dependentDestructuredVariables.errors.txt @@ -0,0 +1,324 @@ +tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(240,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. +tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(242,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. +tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(257,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. +tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(259,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. +tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(274,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. +tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(276,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. +tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(291,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. +tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(293,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. + + +==== tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts (8 errors) ==== + type Action = + | { kind: 'A', payload: number } + | { kind: 'B', payload: string }; + + function f10({ kind, payload }: Action) { + if (kind === 'A') { + payload.toFixed(); + } + if (kind === 'B') { + payload.toUpperCase(); + } + } + + function f11(action: Action) { + const { kind, payload } = action; + if (kind === 'A') { + payload.toFixed(); + } + if (kind === 'B') { + payload.toUpperCase(); + } + } + + function f12({ kind, payload }: Action) { + switch (kind) { + case 'A': + payload.toFixed(); + break; + case 'B': + payload.toUpperCase(); + break; + default: + payload; // never + } + } + + type Action2 = + | { kind: 'A', payload: number | undefined } + | { kind: 'B', payload: string | undefined }; + + function f20({ kind, payload }: Action2) { + if (payload) { + if (kind === 'A') { + payload.toFixed(); + } + if (kind === 'B') { + payload.toUpperCase(); + } + } + } + + function f21(action: Action2) { + const { kind, payload } = action; + if (payload) { + if (kind === 'A') { + payload.toFixed(); + } + if (kind === 'B') { + payload.toUpperCase(); + } + } + } + + function f22(action: Action2) { + if (action.payload) { + const { kind, payload } = action; + if (kind === 'A') { + payload.toFixed(); + } + if (kind === 'B') { + payload.toUpperCase(); + } + } + } + + function f23({ kind, payload }: Action2) { + if (payload) { + switch (kind) { + case 'A': + payload.toFixed(); + break; + case 'B': + payload.toUpperCase(); + break; + default: + payload; // never + } + } + } + + type Foo = + | { kind: 'A', isA: true } + | { kind: 'B', isA: false } + | { kind: 'C', isA: false }; + + function f30({ kind, isA }: Foo) { + if (kind === 'A') { + isA; // true + } + if (kind === 'B') { + isA; // false + } + if (kind === 'C') { + isA; // false + } + if (isA) { + kind; // 'A' + } + else { + kind; // 'B' | 'C' + } + } + + type Args = ['A', number] | ['B', string] + + function f40(...[kind, data]: Args) { + if (kind === 'A') { + data.toFixed(); + } + if (kind === 'B') { + data.toUpperCase(); + } + } + + // Repro from #35283 + + interface A { variant: 'a', value: T } + + interface B { variant: 'b', value: Array } + + type AB = A | B; + + declare function printValue(t: T): void; + + declare function printValueList(t: Array): void; + + function unrefined1(ab: AB): void { + const { variant, value } = ab; + if (variant === 'a') { + printValue(value); + } + else { + printValueList(value); + } + } + + // Repro from #38020 + + type Action3 = + | {type: 'add', payload: { toAdd: number } } + | {type: 'remove', payload: { toRemove: number } }; + + const reducerBroken = (state: number, { type, payload }: Action3) => { + switch (type) { + case 'add': + return state + payload.toAdd; + case 'remove': + return state - payload.toRemove; + } + } + + // Repro from #46143 + + declare var it: Iterator; + const { value, done } = it.next(); + if (!done) { + value; // number + } + + // Repro from #46658 + + declare function f50(cb: (...args: Args) => void): void + + f50((kind, data) => { + if (kind === 'A') { + data.toFixed(); + } + if (kind === 'B') { + data.toUpperCase(); + } + }); + + const f51: (...args: ['A', number] | ['B', string]) => void = (kind, payload) => { + if (kind === 'A') { + payload.toFixed(); + } + if (kind === 'B') { + payload.toUpperCase(); + } + }; + + const f52: (...args: ['A', number] | ['B']) => void = (kind, payload?) => { + if (kind === 'A') { + payload.toFixed(); + } + else { + payload; // undefined + } + }; + + declare function readFile(path: string, callback: (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void): void; + + readFile('hello', (err, data) => { + if (err === null) { + data.length; + } + else { + err.message; + } + }); + + type ReducerArgs = ["add", { a: number, b: number }] | ["concat", { firstArr: any[], secondArr: any[] }]; + + const reducer: (...args: ReducerArgs) => void = (op, args) => { + switch (op) { + case "add": + console.log(args.a + args.b); + break; + case "concat": + console.log(args.firstArr.concat(args.secondArr)); + break; + } + } + + reducer("add", { a: 1, b: 3 }); + reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] }); + + // repro from https://github.com/microsoft/TypeScript/pull/47190#issuecomment-1057603588 + + type FooMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): void; + } + + let fooM: FooMethod = { + method(type, cb) { + if (type == 'num') { + cb(123) + ~~~ +!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. + } else { + cb("abc") + ~~~~~ +!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. + } + } + }; + + type FooAsyncMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): Promise; + } + + let fooAsyncM: FooAsyncMethod = { + async method(type, cb) { + if (type == 'num') { + cb(123) + ~~~ +!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. + } else { + cb("abc") + ~~~~~ +!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. + } + } + }; + + type FooGenMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): Generator; + } + + let fooGenM: FooGenMethod = { + *method(type, cb) { + if (type == 'num') { + cb(123) + ~~~ +!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. + } else { + cb("abc") + ~~~~~ +!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. + } + } + }; + + type FooAsyncGenMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): AsyncGenerator; + } + + let fooAsyncGenM: FooAsyncGenMethod = { + async *method(type, cb) { + if (type == 'num') { + cb(123) + ~~~ +!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. + } else { + cb("abc") + ~~~~~ +!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. + } + } + }; + \ No newline at end of file diff --git a/tests/baselines/reference/dependentDestructuredVariables.js b/tests/baselines/reference/dependentDestructuredVariables.js index 2eb7be3d946a4..db312d66704fd 100644 --- a/tests/baselines/reference/dependentDestructuredVariables.js +++ b/tests/baselines/reference/dependentDestructuredVariables.js @@ -225,10 +225,101 @@ const reducer: (...args: ReducerArgs) => void = (op, args) => { reducer("add", { a: 1, b: 3 }); reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] }); + +// repro from https://github.com/microsoft/TypeScript/pull/47190#issuecomment-1057603588 + +type FooMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): void; +} + +let fooM: FooMethod = { + method(type, cb) { + if (type == 'num') { + cb(123) + } else { + cb("abc") + } + } +}; + +type FooAsyncMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): Promise; +} + +let fooAsyncM: FooAsyncMethod = { + async method(type, cb) { + if (type == 'num') { + cb(123) + } else { + cb("abc") + } + } +}; + +type FooGenMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): Generator; +} + +let fooGenM: FooGenMethod = { + *method(type, cb) { + if (type == 'num') { + cb(123) + } else { + cb("abc") + } + } +}; + +type FooAsyncGenMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): AsyncGenerator; +} + +let fooAsyncGenM: FooAsyncGenMethod = { + async *method(type, cb) { + if (type == 'num') { + cb(123) + } else { + cb("abc") + } + } +}; //// [dependentDestructuredVariables.js] "use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } +var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +}; function f10({ kind, payload }) { if (kind === 'A') { payload.toFixed(); @@ -394,6 +485,50 @@ const reducer = (op, args) => { }; reducer("add", { a: 1, b: 3 }); reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] }); +let fooM = { + method(type, cb) { + if (type == 'num') { + cb(123); + } + else { + cb("abc"); + } + } +}; +let fooAsyncM = { + method(type, cb) { + return __awaiter(this, void 0, void 0, function* () { + if (type == 'num') { + cb(123); + } + else { + cb("abc"); + } + }); + } +}; +let fooGenM = { + *method(type, cb) { + if (type == 'num') { + cb(123); + } + else { + cb("abc"); + } + } +}; +let fooAsyncGenM = { + method(type, cb) { + return __asyncGenerator(this, arguments, function* method_1() { + if (type == 'num') { + cb(123); + } + else { + cb("abc"); + } + }); + } +}; //// [dependentDestructuredVariables.d.ts] @@ -469,3 +604,43 @@ declare type ReducerArgs = ["add", { secondArr: any[]; }]; declare const reducer: (...args: ReducerArgs) => void; +declare type FooMethod = { + method(...args: [ + type: "str", + cb: (e: string) => void + ] | [ + type: "num", + cb: (e: number) => void + ]): void; +}; +declare let fooM: FooMethod; +declare type FooAsyncMethod = { + method(...args: [ + type: "str", + cb: (e: string) => void + ] | [ + type: "num", + cb: (e: number) => void + ]): Promise; +}; +declare let fooAsyncM: FooAsyncMethod; +declare type FooGenMethod = { + method(...args: [ + type: "str", + cb: (e: string) => void + ] | [ + type: "num", + cb: (e: number) => void + ]): Generator; +}; +declare let fooGenM: FooGenMethod; +declare type FooAsyncGenMethod = { + method(...args: [ + type: "str", + cb: (e: string) => void + ] | [ + type: "num", + cb: (e: number) => void + ]): AsyncGenerator; +}; +declare let fooAsyncGenM: FooAsyncGenMethod; diff --git a/tests/baselines/reference/dependentDestructuredVariables.symbols b/tests/baselines/reference/dependentDestructuredVariables.symbols index bad7fbf5b690e..af1919fc31226 100644 --- a/tests/baselines/reference/dependentDestructuredVariables.symbols +++ b/tests/baselines/reference/dependentDestructuredVariables.symbols @@ -321,7 +321,7 @@ interface B { variant: 'b', value: Array } >T : Symbol(T, Decl(dependentDestructuredVariables.ts, 128, 12)) >variant : Symbol(B.variant, Decl(dependentDestructuredVariables.ts, 128, 16)) >value : Symbol(B.value, Decl(dependentDestructuredVariables.ts, 128, 30)) ->Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 3 more) >T : Symbol(T, Decl(dependentDestructuredVariables.ts, 128, 12)) type AB = A | B; @@ -342,7 +342,7 @@ declare function printValueList(t: Array): void; >printValueList : Symbol(printValueList, Decl(dependentDestructuredVariables.ts, 132, 43)) >T : Symbol(T, Decl(dependentDestructuredVariables.ts, 134, 32)) >t : Symbol(t, Decl(dependentDestructuredVariables.ts, 134, 35)) ->Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 3 more) >T : Symbol(T, Decl(dependentDestructuredVariables.ts, 134, 32)) function unrefined1(ab: AB): void { @@ -514,7 +514,7 @@ declare function readFile(path: string, callback: (...args: [err: null, data: un >path : Symbol(path, Decl(dependentDestructuredVariables.ts, 200, 26)) >callback : Symbol(callback, Decl(dependentDestructuredVariables.ts, 200, 39)) >args : Symbol(args, Decl(dependentDestructuredVariables.ts, 200, 51)) ->Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) readFile('hello', (err, data) => { >readFile : Symbol(readFile, Decl(dependentDestructuredVariables.ts, 198, 2)) @@ -595,3 +595,160 @@ reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] }); >firstArr : Symbol(firstArr, Decl(dependentDestructuredVariables.ts, 225, 19)) >secondArr : Symbol(secondArr, Decl(dependentDestructuredVariables.ts, 225, 37)) +// repro from https://github.com/microsoft/TypeScript/pull/47190#issuecomment-1057603588 + +type FooMethod = { +>FooMethod : Symbol(FooMethod, Decl(dependentDestructuredVariables.ts, 225, 59)) + + method(...args: +>method : Symbol(method, Decl(dependentDestructuredVariables.ts, 229, 18)) +>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 230, 9)) + + [type: "str", cb: (e: string) => void] | +>e : Symbol(e, Decl(dependentDestructuredVariables.ts, 231, 23)) + + [type: "num", cb: (e: number) => void] +>e : Symbol(e, Decl(dependentDestructuredVariables.ts, 232, 23)) + + ): void; +} + +let fooM: FooMethod = { +>fooM : Symbol(fooM, Decl(dependentDestructuredVariables.ts, 236, 3)) +>FooMethod : Symbol(FooMethod, Decl(dependentDestructuredVariables.ts, 225, 59)) + + method(type, cb) { +>method : Symbol(method, Decl(dependentDestructuredVariables.ts, 236, 23)) +>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 237, 9)) +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 237, 14)) + + if (type == 'num') { +>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 237, 9)) + + cb(123) +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 237, 14)) + + } else { + cb("abc") +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 237, 14)) + } + } +}; + +type FooAsyncMethod = { +>FooAsyncMethod : Symbol(FooAsyncMethod, Decl(dependentDestructuredVariables.ts, 244, 2)) + + method(...args: +>method : Symbol(method, Decl(dependentDestructuredVariables.ts, 246, 23)) +>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 247, 9)) + + [type: "str", cb: (e: string) => void] | +>e : Symbol(e, Decl(dependentDestructuredVariables.ts, 248, 23)) + + [type: "num", cb: (e: number) => void] +>e : Symbol(e, Decl(dependentDestructuredVariables.ts, 249, 23)) + + ): Promise; +>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, --, --)) +} + +let fooAsyncM: FooAsyncMethod = { +>fooAsyncM : Symbol(fooAsyncM, Decl(dependentDestructuredVariables.ts, 253, 3)) +>FooAsyncMethod : Symbol(FooAsyncMethod, Decl(dependentDestructuredVariables.ts, 244, 2)) + + async method(type, cb) { +>method : Symbol(method, Decl(dependentDestructuredVariables.ts, 253, 33)) +>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 254, 15)) +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 254, 20)) + + if (type == 'num') { +>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 254, 15)) + + cb(123) +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 254, 20)) + + } else { + cb("abc") +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 254, 20)) + } + } +}; + +type FooGenMethod = { +>FooGenMethod : Symbol(FooGenMethod, Decl(dependentDestructuredVariables.ts, 261, 2)) + + method(...args: +>method : Symbol(method, Decl(dependentDestructuredVariables.ts, 263, 21)) +>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 264, 9)) + + [type: "str", cb: (e: string) => void] | +>e : Symbol(e, Decl(dependentDestructuredVariables.ts, 265, 23)) + + [type: "num", cb: (e: number) => void] +>e : Symbol(e, Decl(dependentDestructuredVariables.ts, 266, 23)) + + ): Generator; +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) +} + +let fooGenM: FooGenMethod = { +>fooGenM : Symbol(fooGenM, Decl(dependentDestructuredVariables.ts, 270, 3)) +>FooGenMethod : Symbol(FooGenMethod, Decl(dependentDestructuredVariables.ts, 261, 2)) + + *method(type, cb) { +>method : Symbol(method, Decl(dependentDestructuredVariables.ts, 270, 29)) +>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 271, 10)) +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 271, 15)) + + if (type == 'num') { +>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 271, 10)) + + cb(123) +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 271, 15)) + + } else { + cb("abc") +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 271, 15)) + } + } +}; + +type FooAsyncGenMethod = { +>FooAsyncGenMethod : Symbol(FooAsyncGenMethod, Decl(dependentDestructuredVariables.ts, 278, 2)) + + method(...args: +>method : Symbol(method, Decl(dependentDestructuredVariables.ts, 280, 26)) +>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 281, 9)) + + [type: "str", cb: (e: string) => void] | +>e : Symbol(e, Decl(dependentDestructuredVariables.ts, 282, 23)) + + [type: "num", cb: (e: number) => void] +>e : Symbol(e, Decl(dependentDestructuredVariables.ts, 283, 23)) + + ): AsyncGenerator; +>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --)) +} + +let fooAsyncGenM: FooAsyncGenMethod = { +>fooAsyncGenM : Symbol(fooAsyncGenM, Decl(dependentDestructuredVariables.ts, 287, 3)) +>FooAsyncGenMethod : Symbol(FooAsyncGenMethod, Decl(dependentDestructuredVariables.ts, 278, 2)) + + async *method(type, cb) { +>method : Symbol(method, Decl(dependentDestructuredVariables.ts, 287, 39)) +>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 288, 16)) +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 288, 21)) + + if (type == 'num') { +>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 288, 16)) + + cb(123) +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 288, 21)) + + } else { + cb("abc") +>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 288, 21)) + } + } +}; + diff --git a/tests/baselines/reference/dependentDestructuredVariables.types b/tests/baselines/reference/dependentDestructuredVariables.types index a1b16d186ee08..f916ce17b7398 100644 --- a/tests/baselines/reference/dependentDestructuredVariables.types +++ b/tests/baselines/reference/dependentDestructuredVariables.types @@ -677,3 +677,181 @@ reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] }); >3 : 3 >4 : 4 +// repro from https://github.com/microsoft/TypeScript/pull/47190#issuecomment-1057603588 + +type FooMethod = { +>FooMethod : FooMethod + + method(...args: +>method : (...args: [type: "str", cb: (e: string) => void] | [type: "num", cb: (e: number) => void]) => void +>args : [type: "str", cb: (e: string) => void] | [type: "num", cb: (e: number) => void] + + [type: "str", cb: (e: string) => void] | +>e : string + + [type: "num", cb: (e: number) => void] +>e : number + + ): void; +} + +let fooM: FooMethod = { +>fooM : FooMethod +>{ method(type, cb) { if (type == 'num') { cb(123) } else { cb("abc") } }} : { method(type: "str" | "num", cb: ((e: string) => void) | ((e: number) => void)): void; } + + method(type, cb) { +>method : (type: "str" | "num", cb: ((e: string) => void) | ((e: number) => void)) => void +>type : "str" | "num" +>cb : ((e: string) => void) | ((e: number) => void) + + if (type == 'num') { +>type == 'num' : boolean +>type : "str" | "num" +>'num' : "num" + + cb(123) +>cb(123) : void +>cb : ((e: string) => void) | ((e: number) => void) +>123 : 123 + + } else { + cb("abc") +>cb("abc") : void +>cb : ((e: string) => void) | ((e: number) => void) +>"abc" : "abc" + } + } +}; + +type FooAsyncMethod = { +>FooAsyncMethod : FooAsyncMethod + + method(...args: +>method : (...args: [type: "str", cb: (e: string) => void] | [type: "num", cb: (e: number) => void]) => Promise +>args : [type: "str", cb: (e: string) => void] | [type: "num", cb: (e: number) => void] + + [type: "str", cb: (e: string) => void] | +>e : string + + [type: "num", cb: (e: number) => void] +>e : number + + ): Promise; +} + +let fooAsyncM: FooAsyncMethod = { +>fooAsyncM : FooAsyncMethod +>{ async method(type, cb) { if (type == 'num') { cb(123) } else { cb("abc") } }} : { method(type: "str" | "num", cb: ((e: string) => void) | ((e: number) => void)): Promise; } + + async method(type, cb) { +>method : (type: "str" | "num", cb: ((e: string) => void) | ((e: number) => void)) => Promise +>type : "str" | "num" +>cb : ((e: string) => void) | ((e: number) => void) + + if (type == 'num') { +>type == 'num' : boolean +>type : "str" | "num" +>'num' : "num" + + cb(123) +>cb(123) : void +>cb : ((e: string) => void) | ((e: number) => void) +>123 : 123 + + } else { + cb("abc") +>cb("abc") : void +>cb : ((e: string) => void) | ((e: number) => void) +>"abc" : "abc" + } + } +}; + +type FooGenMethod = { +>FooGenMethod : FooGenMethod + + method(...args: +>method : (...args: [type: "str", cb: (e: string) => void] | [type: "num", cb: (e: number) => void]) => Generator +>args : [type: "str", cb: (e: string) => void] | [type: "num", cb: (e: number) => void] + + [type: "str", cb: (e: string) => void] | +>e : string + + [type: "num", cb: (e: number) => void] +>e : number + + ): Generator; +} + +let fooGenM: FooGenMethod = { +>fooGenM : FooGenMethod +>{ *method(type, cb) { if (type == 'num') { cb(123) } else { cb("abc") } }} : { method(type: "str" | "num", cb: ((e: string) => void) | ((e: number) => void)): Generator; } + + *method(type, cb) { +>method : (type: "str" | "num", cb: ((e: string) => void) | ((e: number) => void)) => Generator +>type : "str" | "num" +>cb : ((e: string) => void) | ((e: number) => void) + + if (type == 'num') { +>type == 'num' : boolean +>type : "str" | "num" +>'num' : "num" + + cb(123) +>cb(123) : void +>cb : ((e: string) => void) | ((e: number) => void) +>123 : 123 + + } else { + cb("abc") +>cb("abc") : void +>cb : ((e: string) => void) | ((e: number) => void) +>"abc" : "abc" + } + } +}; + +type FooAsyncGenMethod = { +>FooAsyncGenMethod : FooAsyncGenMethod + + method(...args: +>method : (...args: [type: "str", cb: (e: string) => void] | [type: "num", cb: (e: number) => void]) => AsyncGenerator +>args : [type: "str", cb: (e: string) => void] | [type: "num", cb: (e: number) => void] + + [type: "str", cb: (e: string) => void] | +>e : string + + [type: "num", cb: (e: number) => void] +>e : number + + ): AsyncGenerator; +} + +let fooAsyncGenM: FooAsyncGenMethod = { +>fooAsyncGenM : FooAsyncGenMethod +>{ async *method(type, cb) { if (type == 'num') { cb(123) } else { cb("abc") } }} : { method(type: "str" | "num", cb: ((e: string) => void) | ((e: number) => void)): AsyncGenerator; } + + async *method(type, cb) { +>method : (type: "str" | "num", cb: ((e: string) => void) | ((e: number) => void)) => AsyncGenerator +>type : "str" | "num" +>cb : ((e: string) => void) | ((e: number) => void) + + if (type == 'num') { +>type == 'num' : boolean +>type : "str" | "num" +>'num' : "num" + + cb(123) +>cb(123) : void +>cb : ((e: string) => void) | ((e: number) => void) +>123 : 123 + + } else { + cb("abc") +>cb("abc") : void +>cb : ((e: string) => void) | ((e: number) => void) +>"abc" : "abc" + } + } +}; + diff --git a/tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts b/tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts index 38bcb68d048e8..2b1d64fc3bac8 100644 --- a/tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts +++ b/tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts @@ -1,6 +1,7 @@ // @strict: true // @declaration: true // @target: es2015 +// @lib: esnext, dom type Action = | { kind: 'A', payload: number } @@ -228,3 +229,73 @@ const reducer: (...args: ReducerArgs) => void = (op, args) => { reducer("add", { a: 1, b: 3 }); reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] }); + +// repro from https://github.com/microsoft/TypeScript/pull/47190#issuecomment-1057603588 + +type FooMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): void; +} + +let fooM: FooMethod = { + method(type, cb) { + if (type == 'num') { + cb(123) + } else { + cb("abc") + } + } +}; + +type FooAsyncMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): Promise; +} + +let fooAsyncM: FooAsyncMethod = { + async method(type, cb) { + if (type == 'num') { + cb(123) + } else { + cb("abc") + } + } +}; + +type FooGenMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): Generator; +} + +let fooGenM: FooGenMethod = { + *method(type, cb) { + if (type == 'num') { + cb(123) + } else { + cb("abc") + } + } +}; + +type FooAsyncGenMethod = { + method(...args: + [type: "str", cb: (e: string) => void] | + [type: "num", cb: (e: number) => void] + ): AsyncGenerator; +} + +let fooAsyncGenM: FooAsyncGenMethod = { + async *method(type, cb) { + if (type == 'num') { + cb(123) + } else { + cb("abc") + } + } +}; From b15f40ac5df6bd698167e0cf26c0665c4f8f3468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Fri, 4 Mar 2022 00:09:15 +0100 Subject: [PATCH 2/2] Fixed an issue with dependent parameters within obj methods --- src/compiler/checker.ts | 2 +- .../dependentDestructuredVariables.errors.txt | 324 ------------------ .../dependentDestructuredVariables.types | 16 +- 3 files changed, 9 insertions(+), 333 deletions(-) delete mode 100644 tests/baselines/reference/dependentDestructuredVariables.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9dfb4a7e112c2..4822e2129ae59 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24315,7 +24315,7 @@ namespace ts { } function getCandidateDiscriminantPropertyAccess(expr: Expression) { - if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference)) { + if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference) || isObjectLiteralMethod(reference)) { // When the reference is a binding pattern or function or arrow expression, we are narrowing a pesudo-reference in // getNarrowedTypeOfSymbol. An identifier for a destructuring variable declared in the same binding pattern or // parameter declared in the same parameter list is a candidate. diff --git a/tests/baselines/reference/dependentDestructuredVariables.errors.txt b/tests/baselines/reference/dependentDestructuredVariables.errors.txt deleted file mode 100644 index 5df33eb17fb47..0000000000000 --- a/tests/baselines/reference/dependentDestructuredVariables.errors.txt +++ /dev/null @@ -1,324 +0,0 @@ -tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(240,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. -tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(242,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. -tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(257,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. -tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(259,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. -tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(274,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. -tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(276,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. -tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(291,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. -tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(293,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. - - -==== tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts (8 errors) ==== - type Action = - | { kind: 'A', payload: number } - | { kind: 'B', payload: string }; - - function f10({ kind, payload }: Action) { - if (kind === 'A') { - payload.toFixed(); - } - if (kind === 'B') { - payload.toUpperCase(); - } - } - - function f11(action: Action) { - const { kind, payload } = action; - if (kind === 'A') { - payload.toFixed(); - } - if (kind === 'B') { - payload.toUpperCase(); - } - } - - function f12({ kind, payload }: Action) { - switch (kind) { - case 'A': - payload.toFixed(); - break; - case 'B': - payload.toUpperCase(); - break; - default: - payload; // never - } - } - - type Action2 = - | { kind: 'A', payload: number | undefined } - | { kind: 'B', payload: string | undefined }; - - function f20({ kind, payload }: Action2) { - if (payload) { - if (kind === 'A') { - payload.toFixed(); - } - if (kind === 'B') { - payload.toUpperCase(); - } - } - } - - function f21(action: Action2) { - const { kind, payload } = action; - if (payload) { - if (kind === 'A') { - payload.toFixed(); - } - if (kind === 'B') { - payload.toUpperCase(); - } - } - } - - function f22(action: Action2) { - if (action.payload) { - const { kind, payload } = action; - if (kind === 'A') { - payload.toFixed(); - } - if (kind === 'B') { - payload.toUpperCase(); - } - } - } - - function f23({ kind, payload }: Action2) { - if (payload) { - switch (kind) { - case 'A': - payload.toFixed(); - break; - case 'B': - payload.toUpperCase(); - break; - default: - payload; // never - } - } - } - - type Foo = - | { kind: 'A', isA: true } - | { kind: 'B', isA: false } - | { kind: 'C', isA: false }; - - function f30({ kind, isA }: Foo) { - if (kind === 'A') { - isA; // true - } - if (kind === 'B') { - isA; // false - } - if (kind === 'C') { - isA; // false - } - if (isA) { - kind; // 'A' - } - else { - kind; // 'B' | 'C' - } - } - - type Args = ['A', number] | ['B', string] - - function f40(...[kind, data]: Args) { - if (kind === 'A') { - data.toFixed(); - } - if (kind === 'B') { - data.toUpperCase(); - } - } - - // Repro from #35283 - - interface A { variant: 'a', value: T } - - interface B { variant: 'b', value: Array } - - type AB = A | B; - - declare function printValue(t: T): void; - - declare function printValueList(t: Array): void; - - function unrefined1(ab: AB): void { - const { variant, value } = ab; - if (variant === 'a') { - printValue(value); - } - else { - printValueList(value); - } - } - - // Repro from #38020 - - type Action3 = - | {type: 'add', payload: { toAdd: number } } - | {type: 'remove', payload: { toRemove: number } }; - - const reducerBroken = (state: number, { type, payload }: Action3) => { - switch (type) { - case 'add': - return state + payload.toAdd; - case 'remove': - return state - payload.toRemove; - } - } - - // Repro from #46143 - - declare var it: Iterator; - const { value, done } = it.next(); - if (!done) { - value; // number - } - - // Repro from #46658 - - declare function f50(cb: (...args: Args) => void): void - - f50((kind, data) => { - if (kind === 'A') { - data.toFixed(); - } - if (kind === 'B') { - data.toUpperCase(); - } - }); - - const f51: (...args: ['A', number] | ['B', string]) => void = (kind, payload) => { - if (kind === 'A') { - payload.toFixed(); - } - if (kind === 'B') { - payload.toUpperCase(); - } - }; - - const f52: (...args: ['A', number] | ['B']) => void = (kind, payload?) => { - if (kind === 'A') { - payload.toFixed(); - } - else { - payload; // undefined - } - }; - - declare function readFile(path: string, callback: (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void): void; - - readFile('hello', (err, data) => { - if (err === null) { - data.length; - } - else { - err.message; - } - }); - - type ReducerArgs = ["add", { a: number, b: number }] | ["concat", { firstArr: any[], secondArr: any[] }]; - - const reducer: (...args: ReducerArgs) => void = (op, args) => { - switch (op) { - case "add": - console.log(args.a + args.b); - break; - case "concat": - console.log(args.firstArr.concat(args.secondArr)); - break; - } - } - - reducer("add", { a: 1, b: 3 }); - reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] }); - - // repro from https://github.com/microsoft/TypeScript/pull/47190#issuecomment-1057603588 - - type FooMethod = { - method(...args: - [type: "str", cb: (e: string) => void] | - [type: "num", cb: (e: number) => void] - ): void; - } - - let fooM: FooMethod = { - method(type, cb) { - if (type == 'num') { - cb(123) - ~~~ -!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. - } else { - cb("abc") - ~~~~~ -!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. - } - } - }; - - type FooAsyncMethod = { - method(...args: - [type: "str", cb: (e: string) => void] | - [type: "num", cb: (e: number) => void] - ): Promise; - } - - let fooAsyncM: FooAsyncMethod = { - async method(type, cb) { - if (type == 'num') { - cb(123) - ~~~ -!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. - } else { - cb("abc") - ~~~~~ -!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. - } - } - }; - - type FooGenMethod = { - method(...args: - [type: "str", cb: (e: string) => void] | - [type: "num", cb: (e: number) => void] - ): Generator; - } - - let fooGenM: FooGenMethod = { - *method(type, cb) { - if (type == 'num') { - cb(123) - ~~~ -!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. - } else { - cb("abc") - ~~~~~ -!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. - } - } - }; - - type FooAsyncGenMethod = { - method(...args: - [type: "str", cb: (e: string) => void] | - [type: "num", cb: (e: number) => void] - ): AsyncGenerator; - } - - let fooAsyncGenM: FooAsyncGenMethod = { - async *method(type, cb) { - if (type == 'num') { - cb(123) - ~~~ -!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'. - } else { - cb("abc") - ~~~~~ -!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. - } - } - }; - \ No newline at end of file diff --git a/tests/baselines/reference/dependentDestructuredVariables.types b/tests/baselines/reference/dependentDestructuredVariables.types index f916ce17b7398..3f0ee678974f8 100644 --- a/tests/baselines/reference/dependentDestructuredVariables.types +++ b/tests/baselines/reference/dependentDestructuredVariables.types @@ -711,13 +711,13 @@ let fooM: FooMethod = { cb(123) >cb(123) : void ->cb : ((e: string) => void) | ((e: number) => void) +>cb : (e: number) => void >123 : 123 } else { cb("abc") >cb("abc") : void ->cb : ((e: string) => void) | ((e: number) => void) +>cb : (e: string) => void >"abc" : "abc" } } @@ -755,13 +755,13 @@ let fooAsyncM: FooAsyncMethod = { cb(123) >cb(123) : void ->cb : ((e: string) => void) | ((e: number) => void) +>cb : (e: number) => void >123 : 123 } else { cb("abc") >cb("abc") : void ->cb : ((e: string) => void) | ((e: number) => void) +>cb : (e: string) => void >"abc" : "abc" } } @@ -799,13 +799,13 @@ let fooGenM: FooGenMethod = { cb(123) >cb(123) : void ->cb : ((e: string) => void) | ((e: number) => void) +>cb : (e: number) => void >123 : 123 } else { cb("abc") >cb("abc") : void ->cb : ((e: string) => void) | ((e: number) => void) +>cb : (e: string) => void >"abc" : "abc" } } @@ -843,13 +843,13 @@ let fooAsyncGenM: FooAsyncGenMethod = { cb(123) >cb(123) : void ->cb : ((e: string) => void) | ((e: number) => void) +>cb : (e: number) => void >123 : 123 } else { cb("abc") >cb("abc") : void ->cb : ((e: string) => void) | ((e: number) => void) +>cb : (e: string) => void >"abc" : "abc" } }