mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-13 06:20:23 -06:00
Fix blocking of recursive dependencies in getNarrowedTypeOfSymbol (#48941)
* Better blocking of recursive dependencies in getNarrowedTypeOfSymbol * Add regression test
This commit is contained in:
parent
8f56f6b49d
commit
38c14606b4
@ -25528,7 +25528,7 @@ namespace ts {
|
||||
if (isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) {
|
||||
const parent = declaration.parent.parent;
|
||||
if (parent.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlags(declaration) & NodeFlags.Const || parent.kind === SyntaxKind.Parameter) {
|
||||
const links = getNodeLinks(location);
|
||||
const links = getNodeLinks(parent);
|
||||
if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
|
||||
links.flags |= NodeCheckFlags.InCheckIdentifier;
|
||||
const parentType = getTypeForBindingElementParent(parent, CheckMode.Normal);
|
||||
|
||||
@ -0,0 +1,334 @@
|
||||
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(314,5): error TS7022: 'value1' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
||||
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(314,5): error TS7031: Binding element 'value1' implicitly has an 'any' type.
|
||||
|
||||
|
||||
==== tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts (2 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<T> { variant: 'a', value: T }
|
||||
|
||||
interface B<T> { variant: 'b', value: Array<T> }
|
||||
|
||||
type AB<T> = A<T> | B<T>;
|
||||
|
||||
declare function printValue<T>(t: T): void;
|
||||
|
||||
declare function printValueList<T>(t: Array<T>): void;
|
||||
|
||||
function unrefined1<T>(ab: AB<T>): void {
|
||||
const { variant, value } = ab;
|
||||
if (variant === 'a') {
|
||||
printValue<T>(value);
|
||||
}
|
||||
else {
|
||||
printValueList<T>(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<number>;
|
||||
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)
|
||||
} 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")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Repro from #48345
|
||||
|
||||
type Func = <T extends ["a", number] | ["b", string]>(...args: T) => void;
|
||||
|
||||
const f60: Func = (kind, payload) => {
|
||||
if (kind === "a") {
|
||||
payload.toFixed(); // error
|
||||
}
|
||||
if (kind === "b") {
|
||||
payload.toUpperCase(); // error
|
||||
}
|
||||
};
|
||||
|
||||
// Repro from #48902
|
||||
|
||||
function foo({
|
||||
value1,
|
||||
~~~~~~
|
||||
!!! error TS7022: 'value1' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
|
||||
~~~~~~
|
||||
!!! error TS7031: Binding element 'value1' implicitly has an 'any' type.
|
||||
test1 = value1.test1,
|
||||
test2 = value1.test2,
|
||||
test3 = value1.test3,
|
||||
test4 = value1.test4,
|
||||
test5 = value1.test5,
|
||||
test6 = value1.test6,
|
||||
test7 = value1.test7,
|
||||
test8 = value1.test8,
|
||||
test9 = value1.test9
|
||||
}) {}
|
||||
|
||||
@ -308,6 +308,21 @@ const f60: Func = (kind, payload) => {
|
||||
payload.toUpperCase(); // error
|
||||
}
|
||||
};
|
||||
|
||||
// Repro from #48902
|
||||
|
||||
function foo({
|
||||
value1,
|
||||
test1 = value1.test1,
|
||||
test2 = value1.test2,
|
||||
test3 = value1.test3,
|
||||
test4 = value1.test4,
|
||||
test5 = value1.test5,
|
||||
test6 = value1.test6,
|
||||
test7 = value1.test7,
|
||||
test8 = value1.test8,
|
||||
test9 = value1.test9
|
||||
}) {}
|
||||
|
||||
|
||||
//// [dependentDestructuredVariables.js]
|
||||
@ -550,6 +565,8 @@ const f60 = (kind, payload) => {
|
||||
payload.toUpperCase(); // error
|
||||
}
|
||||
};
|
||||
// Repro from #48902
|
||||
function foo({ value1, test1 = value1.test1, test2 = value1.test2, test3 = value1.test3, test4 = value1.test4, test5 = value1.test5, test6 = value1.test6, test7 = value1.test7, test8 = value1.test8, test9 = value1.test9 }) { }
|
||||
|
||||
|
||||
//// [dependentDestructuredVariables.d.ts]
|
||||
@ -667,3 +684,15 @@ declare type FooAsyncGenMethod = {
|
||||
declare let fooAsyncGenM: FooAsyncGenMethod;
|
||||
declare type Func = <T extends ["a", number] | ["b", string]>(...args: T) => void;
|
||||
declare const f60: Func;
|
||||
declare function foo({ value1, test1, test2, test3, test4, test5, test6, test7, test8, test9 }: {
|
||||
value1: any;
|
||||
test1?: any;
|
||||
test2?: any;
|
||||
test3?: any;
|
||||
test4?: any;
|
||||
test5?: any;
|
||||
test6?: any;
|
||||
test7?: any;
|
||||
test8?: any;
|
||||
test9?: any;
|
||||
}): void;
|
||||
|
||||
@ -784,3 +784,49 @@ const f60: Func = (kind, payload) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Repro from #48902
|
||||
|
||||
function foo({
|
||||
>foo : Symbol(foo, Decl(dependentDestructuredVariables.ts, 308, 2))
|
||||
|
||||
value1,
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
test1 = value1.test1,
|
||||
>test1 : Symbol(test1, Decl(dependentDestructuredVariables.ts, 313, 11))
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
test2 = value1.test2,
|
||||
>test2 : Symbol(test2, Decl(dependentDestructuredVariables.ts, 314, 25))
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
test3 = value1.test3,
|
||||
>test3 : Symbol(test3, Decl(dependentDestructuredVariables.ts, 315, 25))
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
test4 = value1.test4,
|
||||
>test4 : Symbol(test4, Decl(dependentDestructuredVariables.ts, 316, 25))
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
test5 = value1.test5,
|
||||
>test5 : Symbol(test5, Decl(dependentDestructuredVariables.ts, 317, 25))
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
test6 = value1.test6,
|
||||
>test6 : Symbol(test6, Decl(dependentDestructuredVariables.ts, 318, 25))
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
test7 = value1.test7,
|
||||
>test7 : Symbol(test7, Decl(dependentDestructuredVariables.ts, 319, 25))
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
test8 = value1.test8,
|
||||
>test8 : Symbol(test8, Decl(dependentDestructuredVariables.ts, 320, 25))
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
test9 = value1.test9
|
||||
>test9 : Symbol(test9, Decl(dependentDestructuredVariables.ts, 321, 25))
|
||||
>value1 : Symbol(value1, Decl(dependentDestructuredVariables.ts, 312, 14))
|
||||
|
||||
}) {}
|
||||
|
||||
|
||||
@ -891,3 +891,67 @@ const f60: Func = (kind, payload) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Repro from #48902
|
||||
|
||||
function foo({
|
||||
>foo : ({ value1, test1, test2, test3, test4, test5, test6, test7, test8, test9 }: { value1: any; test1?: any; test2?: any; test3?: any; test4?: any; test5?: any; test6?: any; test7?: any; test8?: any; test9?: any; }) => void
|
||||
|
||||
value1,
|
||||
>value1 : any
|
||||
|
||||
test1 = value1.test1,
|
||||
>test1 : any
|
||||
>value1.test1 : any
|
||||
>value1 : any
|
||||
>test1 : any
|
||||
|
||||
test2 = value1.test2,
|
||||
>test2 : any
|
||||
>value1.test2 : any
|
||||
>value1 : any
|
||||
>test2 : any
|
||||
|
||||
test3 = value1.test3,
|
||||
>test3 : any
|
||||
>value1.test3 : any
|
||||
>value1 : any
|
||||
>test3 : any
|
||||
|
||||
test4 = value1.test4,
|
||||
>test4 : any
|
||||
>value1.test4 : any
|
||||
>value1 : any
|
||||
>test4 : any
|
||||
|
||||
test5 = value1.test5,
|
||||
>test5 : any
|
||||
>value1.test5 : any
|
||||
>value1 : any
|
||||
>test5 : any
|
||||
|
||||
test6 = value1.test6,
|
||||
>test6 : any
|
||||
>value1.test6 : any
|
||||
>value1 : any
|
||||
>test6 : any
|
||||
|
||||
test7 = value1.test7,
|
||||
>test7 : any
|
||||
>value1.test7 : any
|
||||
>value1 : any
|
||||
>test7 : any
|
||||
|
||||
test8 = value1.test8,
|
||||
>test8 : any
|
||||
>value1.test8 : any
|
||||
>value1 : any
|
||||
>test8 : any
|
||||
|
||||
test9 = value1.test9
|
||||
>test9 : any
|
||||
>value1.test9 : any
|
||||
>value1 : any
|
||||
>test9 : any
|
||||
|
||||
}) {}
|
||||
|
||||
|
||||
@ -312,3 +312,18 @@ const f60: Func = (kind, payload) => {
|
||||
payload.toUpperCase(); // error
|
||||
}
|
||||
};
|
||||
|
||||
// Repro from #48902
|
||||
|
||||
function foo({
|
||||
value1,
|
||||
test1 = value1.test1,
|
||||
test2 = value1.test2,
|
||||
test3 = value1.test3,
|
||||
test4 = value1.test4,
|
||||
test5 = value1.test5,
|
||||
test6 = value1.test6,
|
||||
test7 = value1.test7,
|
||||
test8 = value1.test8,
|
||||
test9 = value1.test9
|
||||
}) {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user