Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/dependent parameters obj methods #48110

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24315,7 +24315,7 @@ namespace ts {
}

function getCandidateDiscriminantPropertyAccess(expr: Expression) {
if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference)) {
if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(reference) || isObjectLiteralMethod(reference)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still different function-like types - but I couldn't repro this with like class methods etc and for some types (like getters/setters it doesn't make sense in the first place).

The issue is manifesting itself only if parameters are declared in the most straightforward way, so like in here:

const obj = { method(a, b) {} }

and when "dependent parameters" are flowing into them from an assignment to a particular type or something (there are no "inline" types declared for them). I couldn't reproduce this behavior with classes anyhow, nor with constructors - as param types don't seem to "flow" into them, they have to be declared explicitly. I might have missed some way to make those actually flow, maybe there is some combination of features that allows that I didn't think of 🤷‍♂️

// 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.
Expand Down
175 changes: 175 additions & 0 deletions tests/baselines/reference/dependentDestructuredVariables.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<any>;
}

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<any, any, any>;
}

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<any, any, any>;
}

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();
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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<any>;
};
declare let fooAsyncM: FooAsyncMethod;
declare type FooGenMethod = {
method(...args: [
type: "str",
cb: (e: string) => void
] | [
type: "num",
cb: (e: number) => void
]): Generator<any, any, any>;
};
declare let fooGenM: FooGenMethod;
declare type FooAsyncGenMethod = {
method(...args: [
type: "str",
cb: (e: string) => void
] | [
type: "num",
cb: (e: number) => void
]): AsyncGenerator<any, any, any>;
};
declare let fooAsyncGenM: FooAsyncGenMethod;
163 changes: 160 additions & 3 deletions tests/baselines/reference/dependentDestructuredVariables.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ interface B<T> { variant: 'b', value: Array<T> }
>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<T> = A<T> | B<T>;
Expand All @@ -342,7 +342,7 @@ declare function printValueList<T>(t: Array<T>): 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<T>(ab: AB<T>): void {
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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<any>;
>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<any, any, any>;
>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<any, any, any>;
>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))
}
}
};

Loading