mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Control flow analysis for dependent parameters (#47190)
* Support control flow analysis for dependent parameters * Add tests
This commit is contained in:
parent
2f058b72d6
commit
f4e1efbc29
@ -22694,11 +22694,12 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function getAccessedPropertyName(access: AccessExpression | BindingElement): __String | undefined {
|
||||
function getAccessedPropertyName(access: AccessExpression | BindingElement | ParameterDeclaration): __String | undefined {
|
||||
let propertyName;
|
||||
return access.kind === SyntaxKind.PropertyAccessExpression ? access.name.escapedText :
|
||||
access.kind === SyntaxKind.ElementAccessExpression && isStringOrNumericLiteralLike(access.argumentExpression) ? escapeLeadingUnderscores(access.argumentExpression.text) :
|
||||
access.kind === SyntaxKind.BindingElement && (propertyName = getDestructuringPropertyName(access)) ? escapeLeadingUnderscores(propertyName) :
|
||||
access.kind === SyntaxKind.Parameter ? ("" + access.parent.parameters.indexOf(access)) as __String :
|
||||
undefined;
|
||||
}
|
||||
|
||||
@ -24120,13 +24121,14 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getCandidateDiscriminantPropertyAccess(expr: Expression) {
|
||||
if (isBindingPattern(reference)) {
|
||||
// When the reference is a binding pattern, we are narrowing a pesudo-reference in getNarrowedTypeOfSymbol.
|
||||
// An identifier for a destructuring variable declared in the same binding pattern is a candidate.
|
||||
if (isBindingPattern(reference) || isFunctionExpressionOrArrowFunction(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.
|
||||
if (isIdentifier(expr)) {
|
||||
const symbol = getResolvedSymbol(expr);
|
||||
const declaration = symbol.valueDeclaration;
|
||||
if (declaration && isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && reference === declaration.parent) {
|
||||
if (declaration && (isBindingElement(declaration) || isParameter(declaration)) && reference === declaration.parent && !declaration.initializer && !declaration.dotDotDotToken) {
|
||||
return declaration;
|
||||
}
|
||||
}
|
||||
@ -24173,7 +24175,7 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function narrowTypeByDiscriminant(type: Type, access: AccessExpression | BindingElement, narrowType: (t: Type) => Type): Type {
|
||||
function narrowTypeByDiscriminant(type: Type, access: AccessExpression | BindingElement | ParameterDeclaration, narrowType: (t: Type) => Type): Type {
|
||||
const propName = getAccessedPropertyName(access);
|
||||
if (propName === undefined) {
|
||||
return type;
|
||||
@ -24191,7 +24193,7 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function narrowTypeByDiscriminantProperty(type: Type, access: AccessExpression | BindingElement, operator: SyntaxKind, value: Expression, assumeTrue: boolean) {
|
||||
function narrowTypeByDiscriminantProperty(type: Type, access: AccessExpression | BindingElement | ParameterDeclaration, operator: SyntaxKind, value: Expression, assumeTrue: boolean) {
|
||||
if ((operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) && type.flags & TypeFlags.Union) {
|
||||
const keyPropertyName = getKeyPropertyName(type as UnionType);
|
||||
if (keyPropertyName && keyPropertyName === getAccessedPropertyName(access)) {
|
||||
@ -24206,7 +24208,7 @@ namespace ts {
|
||||
return narrowTypeByDiscriminant(type, access, t => narrowTypeByEquality(t, operator, value, assumeTrue));
|
||||
}
|
||||
|
||||
function narrowTypeBySwitchOnDiscriminantProperty(type: Type, access: AccessExpression | BindingElement, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
|
||||
function narrowTypeBySwitchOnDiscriminantProperty(type: Type, access: AccessExpression | BindingElement | ParameterDeclaration, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
|
||||
if (clauseStart < clauseEnd && type.flags & TypeFlags.Union && getKeyPropertyName(type as UnionType) === getAccessedPropertyName(access)) {
|
||||
const clauseTypes = getSwitchClauseTypes(switchStatement).slice(clauseStart, clauseEnd);
|
||||
const candidate = getUnionType(map(clauseTypes, t => getConstituentTypeForKeyType(type as UnionType, t) || unknownType));
|
||||
@ -24984,42 +24986,78 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getNarrowedTypeOfSymbol(symbol: Symbol, location: Identifier) {
|
||||
// If we have a non-rest binding element with no initializer declared as a const variable or a const-like
|
||||
// parameter (a parameter for which there are no assignments in the function body), and if the parent type
|
||||
// for the destructuring is a union type, one or more of the binding elements may represent discriminant
|
||||
// properties, and we want the effects of conditional checks on such discriminants to affect the types of
|
||||
// other binding elements from the same destructuring. Consider:
|
||||
//
|
||||
// type Action =
|
||||
// | { kind: 'A', payload: number }
|
||||
// | { kind: 'B', payload: string };
|
||||
//
|
||||
// function f1({ kind, payload }: Action) {
|
||||
// if (kind === 'A') {
|
||||
// payload.toFixed();
|
||||
// }
|
||||
// if (kind === 'B') {
|
||||
// payload.toUpperCase();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
|
||||
// the binding pattern AST instance for '{ kind, payload }' as a pseudo-reference and narrow this reference
|
||||
// as if it occurred in the specified location. We then recompute the narrowed binding element type by
|
||||
// destructuring from the narrowed parent type.
|
||||
const declaration = symbol.valueDeclaration;
|
||||
if (declaration && 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);
|
||||
if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
|
||||
links.flags |= NodeCheckFlags.InCheckIdentifier;
|
||||
const parentType = getTypeForBindingElementParent(parent);
|
||||
links.flags &= ~NodeCheckFlags.InCheckIdentifier;
|
||||
if (parentType && parentType.flags & TypeFlags.Union && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) {
|
||||
const pattern = declaration.parent;
|
||||
const narrowedType = getFlowTypeOfReference(pattern, parentType, parentType, /*flowContainer*/ undefined, location.flowNode);
|
||||
return getBindingElementTypeFromParentType(declaration, narrowedType);
|
||||
if (declaration) {
|
||||
// If we have a non-rest binding element with no initializer declared as a const variable or a const-like
|
||||
// parameter (a parameter for which there are no assignments in the function body), and if the parent type
|
||||
// for the destructuring is a union type, one or more of the binding elements may represent discriminant
|
||||
// properties, and we want the effects of conditional checks on such discriminants to affect the types of
|
||||
// other binding elements from the same destructuring. Consider:
|
||||
//
|
||||
// type Action =
|
||||
// | { kind: 'A', payload: number }
|
||||
// | { kind: 'B', payload: string };
|
||||
//
|
||||
// function f({ kind, payload }: Action) {
|
||||
// if (kind === 'A') {
|
||||
// payload.toFixed();
|
||||
// }
|
||||
// if (kind === 'B') {
|
||||
// payload.toUpperCase();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
|
||||
// the binding pattern AST instance for '{ kind, payload }' as a pseudo-reference and narrow this reference
|
||||
// as if it occurred in the specified location. We then recompute the narrowed binding element type by
|
||||
// destructuring from the narrowed parent type.
|
||||
if (isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) {
|
||||
const parent = declaration.parent.parent;
|
||||
if (parent.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlags(declaration) & NodeFlags.Const || parent.kind === SyntaxKind.Parameter) {
|
||||
const links = getNodeLinks(location);
|
||||
if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
|
||||
links.flags |= NodeCheckFlags.InCheckIdentifier;
|
||||
const parentType = getTypeForBindingElementParent(parent);
|
||||
links.flags &= ~NodeCheckFlags.InCheckIdentifier;
|
||||
if (parentType && parentType.flags & TypeFlags.Union && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) {
|
||||
const pattern = declaration.parent;
|
||||
const narrowedType = getFlowTypeOfReference(pattern, parentType, parentType, /*flowContainer*/ undefined, location.flowNode);
|
||||
return getBindingElementTypeFromParentType(declaration, narrowedType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we have a const-like parameter with no type annotation or initializer, and if the parameter is contextually
|
||||
// typed by a signature with a single rest parameter of a union of tuple types, one or more of the parameters may
|
||||
// represent discriminant tuple elements, and we want the effects of conditional checks on such discriminants to
|
||||
// affect the types of other parameters in the same parameter list. Consider:
|
||||
//
|
||||
// type Action = [kind: 'A', payload: number] | [kind: 'B', payload: string];
|
||||
//
|
||||
// const f: (...args: Action) => void = (kind, payload) => {
|
||||
// if (kind === 'A') {
|
||||
// payload.toFixed();
|
||||
// }
|
||||
// if (kind === 'B') {
|
||||
// payload.toUpperCase();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
|
||||
// the arrow function AST node for '(kind, payload) => ...' as a pseudo-reference and narrow this reference as
|
||||
// if it occurred in the specified location. We then recompute the narrowed parameter type by indexing into the
|
||||
// narrowed tuple type.
|
||||
if (isParameter(declaration) && !declaration.type && !declaration.initializer && !declaration.dotDotDotToken) {
|
||||
const func = declaration.parent;
|
||||
if (func.parameters.length >= 2 && isContextSensitiveFunctionOrObjectLiteralMethod(func)) {
|
||||
const contextualSignature = getContextualSignature(func);
|
||||
if (contextualSignature && contextualSignature.parameters.length === 1 && signatureHasRestParameter(contextualSignature)) {
|
||||
const restType = getTypeOfSymbol(contextualSignature.parameters[0]);
|
||||
if (restType.flags & TypeFlags.Union && everyType(restType, isTupleType) && !isSymbolAssigned(symbol)) {
|
||||
const narrowedType = getFlowTypeOfReference(func, restType, restType, /*flowContainer*/ undefined, location.flowNode);
|
||||
const index = func.parameters.indexOf(declaration) - (getThisParameter(func) ? 1 : 0);
|
||||
return getIndexedAccessType(narrowedType, getNumberLiteralType(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,6 +167,64 @@ 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] });
|
||||
|
||||
|
||||
//// [dependentDestructuredVariables.js]
|
||||
@ -292,6 +350,50 @@ const { value, done } = it.next();
|
||||
if (!done) {
|
||||
value; // number
|
||||
}
|
||||
f50((kind, data) => {
|
||||
if (kind === 'A') {
|
||||
data.toFixed();
|
||||
}
|
||||
if (kind === 'B') {
|
||||
data.toUpperCase();
|
||||
}
|
||||
});
|
||||
const f51 = (kind, payload) => {
|
||||
if (kind === 'A') {
|
||||
payload.toFixed();
|
||||
}
|
||||
if (kind === 'B') {
|
||||
payload.toUpperCase();
|
||||
}
|
||||
};
|
||||
const f52 = (kind, payload) => {
|
||||
if (kind === 'A') {
|
||||
payload.toFixed();
|
||||
}
|
||||
else {
|
||||
payload; // undefined
|
||||
}
|
||||
};
|
||||
readFile('hello', (err, data) => {
|
||||
if (err === null) {
|
||||
data.length;
|
||||
}
|
||||
else {
|
||||
err.message;
|
||||
}
|
||||
});
|
||||
const reducer = (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] });
|
||||
|
||||
|
||||
//// [dependentDestructuredVariables.d.ts]
|
||||
@ -355,3 +457,15 @@ declare type Action3 = {
|
||||
declare const reducerBroken: (state: number, { type, payload }: Action3) => number;
|
||||
declare var it: Iterator<number>;
|
||||
declare const value: any, done: boolean | undefined;
|
||||
declare function f50(cb: (...args: Args) => void): void;
|
||||
declare const f51: (...args: ['A', number] | ['B', string]) => void;
|
||||
declare const f52: (...args: ['A', number] | ['B']) => void;
|
||||
declare function readFile(path: string, callback: (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void): void;
|
||||
declare type ReducerArgs = ["add", {
|
||||
a: number;
|
||||
b: number;
|
||||
}] | ["concat", {
|
||||
firstArr: any[];
|
||||
secondArr: any[];
|
||||
}];
|
||||
declare const reducer: (...args: ReducerArgs) => void;
|
||||
|
||||
@ -434,3 +434,164 @@ if (!done) {
|
||||
>value : Symbol(value, Decl(dependentDestructuredVariables.ts, 164, 7))
|
||||
}
|
||||
|
||||
// Repro from #46658
|
||||
|
||||
declare function f50(cb: (...args: Args) => void): void
|
||||
>f50 : Symbol(f50, Decl(dependentDestructuredVariables.ts, 167, 1))
|
||||
>cb : Symbol(cb, Decl(dependentDestructuredVariables.ts, 171, 21))
|
||||
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 171, 26))
|
||||
>Args : Symbol(Args, Decl(dependentDestructuredVariables.ts, 111, 1))
|
||||
|
||||
f50((kind, data) => {
|
||||
>f50 : Symbol(f50, Decl(dependentDestructuredVariables.ts, 167, 1))
|
||||
>kind : Symbol(kind, Decl(dependentDestructuredVariables.ts, 173, 5))
|
||||
>data : Symbol(data, Decl(dependentDestructuredVariables.ts, 173, 10))
|
||||
|
||||
if (kind === 'A') {
|
||||
>kind : Symbol(kind, Decl(dependentDestructuredVariables.ts, 173, 5))
|
||||
|
||||
data.toFixed();
|
||||
>data.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
|
||||
>data : Symbol(data, Decl(dependentDestructuredVariables.ts, 173, 10))
|
||||
>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
if (kind === 'B') {
|
||||
>kind : Symbol(kind, Decl(dependentDestructuredVariables.ts, 173, 5))
|
||||
|
||||
data.toUpperCase();
|
||||
>data.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
>data : Symbol(data, Decl(dependentDestructuredVariables.ts, 173, 10))
|
||||
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
});
|
||||
|
||||
const f51: (...args: ['A', number] | ['B', string]) => void = (kind, payload) => {
|
||||
>f51 : Symbol(f51, Decl(dependentDestructuredVariables.ts, 182, 5))
|
||||
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 182, 12))
|
||||
>kind : Symbol(kind, Decl(dependentDestructuredVariables.ts, 182, 63))
|
||||
>payload : Symbol(payload, Decl(dependentDestructuredVariables.ts, 182, 68))
|
||||
|
||||
if (kind === 'A') {
|
||||
>kind : Symbol(kind, Decl(dependentDestructuredVariables.ts, 182, 63))
|
||||
|
||||
payload.toFixed();
|
||||
>payload.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
|
||||
>payload : Symbol(payload, Decl(dependentDestructuredVariables.ts, 182, 68))
|
||||
>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
if (kind === 'B') {
|
||||
>kind : Symbol(kind, Decl(dependentDestructuredVariables.ts, 182, 63))
|
||||
|
||||
payload.toUpperCase();
|
||||
>payload.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
>payload : Symbol(payload, Decl(dependentDestructuredVariables.ts, 182, 68))
|
||||
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
};
|
||||
|
||||
const f52: (...args: ['A', number] | ['B']) => void = (kind, payload?) => {
|
||||
>f52 : Symbol(f52, Decl(dependentDestructuredVariables.ts, 191, 5))
|
||||
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 191, 12))
|
||||
>kind : Symbol(kind, Decl(dependentDestructuredVariables.ts, 191, 55))
|
||||
>payload : Symbol(payload, Decl(dependentDestructuredVariables.ts, 191, 60))
|
||||
|
||||
if (kind === 'A') {
|
||||
>kind : Symbol(kind, Decl(dependentDestructuredVariables.ts, 191, 55))
|
||||
|
||||
payload.toFixed();
|
||||
>payload.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
|
||||
>payload : Symbol(payload, Decl(dependentDestructuredVariables.ts, 191, 60))
|
||||
>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
else {
|
||||
payload; // undefined
|
||||
>payload : Symbol(payload, Decl(dependentDestructuredVariables.ts, 191, 60))
|
||||
}
|
||||
};
|
||||
|
||||
declare function readFile(path: string, callback: (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void): void;
|
||||
>readFile : Symbol(readFile, Decl(dependentDestructuredVariables.ts, 198, 2))
|
||||
>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, --, --))
|
||||
|
||||
readFile('hello', (err, data) => {
|
||||
>readFile : Symbol(readFile, Decl(dependentDestructuredVariables.ts, 198, 2))
|
||||
>err : Symbol(err, Decl(dependentDestructuredVariables.ts, 202, 19))
|
||||
>data : Symbol(data, Decl(dependentDestructuredVariables.ts, 202, 23))
|
||||
|
||||
if (err === null) {
|
||||
>err : Symbol(err, Decl(dependentDestructuredVariables.ts, 202, 19))
|
||||
|
||||
data.length;
|
||||
>data.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
>data : Symbol(data, Decl(dependentDestructuredVariables.ts, 202, 23))
|
||||
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
else {
|
||||
err.message;
|
||||
>err.message : Symbol(Error.message, Decl(lib.es5.d.ts, --, --))
|
||||
>err : Symbol(err, Decl(dependentDestructuredVariables.ts, 202, 19))
|
||||
>message : Symbol(Error.message, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
});
|
||||
|
||||
type ReducerArgs = ["add", { a: number, b: number }] | ["concat", { firstArr: any[], secondArr: any[] }];
|
||||
>ReducerArgs : Symbol(ReducerArgs, Decl(dependentDestructuredVariables.ts, 209, 3))
|
||||
>a : Symbol(a, Decl(dependentDestructuredVariables.ts, 211, 28))
|
||||
>b : Symbol(b, Decl(dependentDestructuredVariables.ts, 211, 39))
|
||||
>firstArr : Symbol(firstArr, Decl(dependentDestructuredVariables.ts, 211, 67))
|
||||
>secondArr : Symbol(secondArr, Decl(dependentDestructuredVariables.ts, 211, 84))
|
||||
|
||||
const reducer: (...args: ReducerArgs) => void = (op, args) => {
|
||||
>reducer : Symbol(reducer, Decl(dependentDestructuredVariables.ts, 213, 5))
|
||||
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 213, 16))
|
||||
>ReducerArgs : Symbol(ReducerArgs, Decl(dependentDestructuredVariables.ts, 209, 3))
|
||||
>op : Symbol(op, Decl(dependentDestructuredVariables.ts, 213, 49))
|
||||
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 213, 52))
|
||||
|
||||
switch (op) {
|
||||
>op : Symbol(op, Decl(dependentDestructuredVariables.ts, 213, 49))
|
||||
|
||||
case "add":
|
||||
console.log(args.a + args.b);
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>args.a : Symbol(a, Decl(dependentDestructuredVariables.ts, 211, 28))
|
||||
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 213, 52))
|
||||
>a : Symbol(a, Decl(dependentDestructuredVariables.ts, 211, 28))
|
||||
>args.b : Symbol(b, Decl(dependentDestructuredVariables.ts, 211, 39))
|
||||
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 213, 52))
|
||||
>b : Symbol(b, Decl(dependentDestructuredVariables.ts, 211, 39))
|
||||
|
||||
break;
|
||||
case "concat":
|
||||
console.log(args.firstArr.concat(args.secondArr));
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>args.firstArr.concat : Symbol(Array.concat, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>args.firstArr : Symbol(firstArr, Decl(dependentDestructuredVariables.ts, 211, 67))
|
||||
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 213, 52))
|
||||
>firstArr : Symbol(firstArr, Decl(dependentDestructuredVariables.ts, 211, 67))
|
||||
>concat : Symbol(Array.concat, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>args.secondArr : Symbol(secondArr, Decl(dependentDestructuredVariables.ts, 211, 84))
|
||||
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 213, 52))
|
||||
>secondArr : Symbol(secondArr, Decl(dependentDestructuredVariables.ts, 211, 84))
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reducer("add", { a: 1, b: 3 });
|
||||
>reducer : Symbol(reducer, Decl(dependentDestructuredVariables.ts, 213, 5))
|
||||
>a : Symbol(a, Decl(dependentDestructuredVariables.ts, 224, 16))
|
||||
>b : Symbol(b, Decl(dependentDestructuredVariables.ts, 224, 22))
|
||||
|
||||
reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] });
|
||||
>reducer : Symbol(reducer, Decl(dependentDestructuredVariables.ts, 213, 5))
|
||||
>firstArr : Symbol(firstArr, Decl(dependentDestructuredVariables.ts, 225, 19))
|
||||
>secondArr : Symbol(secondArr, Decl(dependentDestructuredVariables.ts, 225, 37))
|
||||
|
||||
|
||||
@ -471,3 +471,209 @@ if (!done) {
|
||||
>value : number
|
||||
}
|
||||
|
||||
// Repro from #46658
|
||||
|
||||
declare function f50(cb: (...args: Args) => void): void
|
||||
>f50 : (cb: (...args: Args) => void) => void
|
||||
>cb : (...args: Args) => void
|
||||
>args : Args
|
||||
|
||||
f50((kind, data) => {
|
||||
>f50((kind, data) => { if (kind === 'A') { data.toFixed(); } if (kind === 'B') { data.toUpperCase(); }}) : void
|
||||
>f50 : (cb: (...args: Args) => void) => void
|
||||
>(kind, data) => { if (kind === 'A') { data.toFixed(); } if (kind === 'B') { data.toUpperCase(); }} : (kind: "A" | "B", data: string | number) => void
|
||||
>kind : "A" | "B"
|
||||
>data : string | number
|
||||
|
||||
if (kind === 'A') {
|
||||
>kind === 'A' : boolean
|
||||
>kind : "A" | "B"
|
||||
>'A' : "A"
|
||||
|
||||
data.toFixed();
|
||||
>data.toFixed() : string
|
||||
>data.toFixed : (fractionDigits?: number | undefined) => string
|
||||
>data : number
|
||||
>toFixed : (fractionDigits?: number | undefined) => string
|
||||
}
|
||||
if (kind === 'B') {
|
||||
>kind === 'B' : boolean
|
||||
>kind : "A" | "B"
|
||||
>'B' : "B"
|
||||
|
||||
data.toUpperCase();
|
||||
>data.toUpperCase() : string
|
||||
>data.toUpperCase : () => string
|
||||
>data : string
|
||||
>toUpperCase : () => string
|
||||
}
|
||||
});
|
||||
|
||||
const f51: (...args: ['A', number] | ['B', string]) => void = (kind, payload) => {
|
||||
>f51 : (...args: ['A', number] | ['B', string]) => void
|
||||
>args : ["A", number] | ["B", string]
|
||||
>(kind, payload) => { if (kind === 'A') { payload.toFixed(); } if (kind === 'B') { payload.toUpperCase(); }} : (kind: "A" | "B", payload: string | number) => void
|
||||
>kind : "A" | "B"
|
||||
>payload : string | number
|
||||
|
||||
if (kind === 'A') {
|
||||
>kind === 'A' : boolean
|
||||
>kind : "A" | "B"
|
||||
>'A' : "A"
|
||||
|
||||
payload.toFixed();
|
||||
>payload.toFixed() : string
|
||||
>payload.toFixed : (fractionDigits?: number | undefined) => string
|
||||
>payload : number
|
||||
>toFixed : (fractionDigits?: number | undefined) => string
|
||||
}
|
||||
if (kind === 'B') {
|
||||
>kind === 'B' : boolean
|
||||
>kind : "A" | "B"
|
||||
>'B' : "B"
|
||||
|
||||
payload.toUpperCase();
|
||||
>payload.toUpperCase() : string
|
||||
>payload.toUpperCase : () => string
|
||||
>payload : string
|
||||
>toUpperCase : () => string
|
||||
}
|
||||
};
|
||||
|
||||
const f52: (...args: ['A', number] | ['B']) => void = (kind, payload?) => {
|
||||
>f52 : (...args: ['A', number] | ['B']) => void
|
||||
>args : ["A", number] | ["B"]
|
||||
>(kind, payload?) => { if (kind === 'A') { payload.toFixed(); } else { payload; // undefined }} : (kind: "A" | "B", payload?: number | undefined) => void
|
||||
>kind : "A" | "B"
|
||||
>payload : number | undefined
|
||||
|
||||
if (kind === 'A') {
|
||||
>kind === 'A' : boolean
|
||||
>kind : "A" | "B"
|
||||
>'A' : "A"
|
||||
|
||||
payload.toFixed();
|
||||
>payload.toFixed() : string
|
||||
>payload.toFixed : (fractionDigits?: number | undefined) => string
|
||||
>payload : number
|
||||
>toFixed : (fractionDigits?: number | undefined) => string
|
||||
}
|
||||
else {
|
||||
payload; // undefined
|
||||
>payload : undefined
|
||||
}
|
||||
};
|
||||
|
||||
declare function readFile(path: string, callback: (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void): void;
|
||||
>readFile : (path: string, callback: (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void) => void
|
||||
>path : string
|
||||
>callback : (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void
|
||||
>args : [err: null, data: unknown[]] | [err: Error, data: undefined]
|
||||
>null : null
|
||||
|
||||
readFile('hello', (err, data) => {
|
||||
>readFile('hello', (err, data) => { if (err === null) { data.length; } else { err.message; }}) : void
|
||||
>readFile : (path: string, callback: (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void) => void
|
||||
>'hello' : "hello"
|
||||
>(err, data) => { if (err === null) { data.length; } else { err.message; }} : (err: Error | null, data: unknown[] | undefined) => void
|
||||
>err : Error | null
|
||||
>data : unknown[] | undefined
|
||||
|
||||
if (err === null) {
|
||||
>err === null : boolean
|
||||
>err : Error | null
|
||||
>null : null
|
||||
|
||||
data.length;
|
||||
>data.length : number
|
||||
>data : unknown[]
|
||||
>length : number
|
||||
}
|
||||
else {
|
||||
err.message;
|
||||
>err.message : string
|
||||
>err : Error
|
||||
>message : string
|
||||
}
|
||||
});
|
||||
|
||||
type ReducerArgs = ["add", { a: number, b: number }] | ["concat", { firstArr: any[], secondArr: any[] }];
|
||||
>ReducerArgs : ReducerArgs
|
||||
>a : number
|
||||
>b : number
|
||||
>firstArr : any[]
|
||||
>secondArr : any[]
|
||||
|
||||
const reducer: (...args: ReducerArgs) => void = (op, args) => {
|
||||
>reducer : (...args: ReducerArgs) => void
|
||||
>args : ReducerArgs
|
||||
>(op, args) => { switch (op) { case "add": console.log(args.a + args.b); break; case "concat": console.log(args.firstArr.concat(args.secondArr)); break; }} : (op: "add" | "concat", args: { a: number; b: number; } | { firstArr: any[]; secondArr: any[]; }) => void
|
||||
>op : "add" | "concat"
|
||||
>args : { a: number; b: number; } | { firstArr: any[]; secondArr: any[]; }
|
||||
|
||||
switch (op) {
|
||||
>op : "add" | "concat"
|
||||
|
||||
case "add":
|
||||
>"add" : "add"
|
||||
|
||||
console.log(args.a + args.b);
|
||||
>console.log(args.a + args.b) : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>args.a + args.b : number
|
||||
>args.a : number
|
||||
>args : { a: number; b: number; }
|
||||
>a : number
|
||||
>args.b : number
|
||||
>args : { a: number; b: number; }
|
||||
>b : number
|
||||
|
||||
break;
|
||||
case "concat":
|
||||
>"concat" : "concat"
|
||||
|
||||
console.log(args.firstArr.concat(args.secondArr));
|
||||
>console.log(args.firstArr.concat(args.secondArr)) : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>args.firstArr.concat(args.secondArr) : any[]
|
||||
>args.firstArr.concat : { (...items: ConcatArray<any>[]): any[]; (...items: any[]): any[]; }
|
||||
>args.firstArr : any[]
|
||||
>args : { firstArr: any[]; secondArr: any[]; }
|
||||
>firstArr : any[]
|
||||
>concat : { (...items: ConcatArray<any>[]): any[]; (...items: any[]): any[]; }
|
||||
>args.secondArr : any[]
|
||||
>args : { firstArr: any[]; secondArr: any[]; }
|
||||
>secondArr : any[]
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reducer("add", { a: 1, b: 3 });
|
||||
>reducer("add", { a: 1, b: 3 }) : void
|
||||
>reducer : (...args: ReducerArgs) => void
|
||||
>"add" : "add"
|
||||
>{ a: 1, b: 3 } : { a: number; b: number; }
|
||||
>a : number
|
||||
>1 : 1
|
||||
>b : number
|
||||
>3 : 3
|
||||
|
||||
reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] });
|
||||
>reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] }) : void
|
||||
>reducer : (...args: ReducerArgs) => void
|
||||
>"concat" : "concat"
|
||||
>{ firstArr: [1, 2], secondArr: [3, 4] } : { firstArr: number[]; secondArr: number[]; }
|
||||
>firstArr : number[]
|
||||
>[1, 2] : number[]
|
||||
>1 : 1
|
||||
>2 : 2
|
||||
>secondArr : number[]
|
||||
>[3, 4] : number[]
|
||||
>3 : 3
|
||||
>4 : 4
|
||||
|
||||
|
||||
@ -170,3 +170,61 @@ 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] });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user