diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9c5beffc61130..6be2c5ca9b401 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -175,12 +175,15 @@ namespace ts { } const enum CheckMode { - Normal = 0, // Normal type checking - Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable - Inferential = 1 << 1, // Inferential typing - SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions - SkipGenericFunctions = 1 << 3, // Skip single signature generic functions - IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help + Normal = 0, // Normal type checking + Contextual = 1 << 0, // Explicitly assigned contextual type, therefore not cacheable + Inferential = 1 << 1, // Inferential typing + SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions + SkipGenericFunctions = 1 << 3, // Skip single signature generic functions + IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help + RestBindingElement = 1 << 5, // Checking a type that is going to be used to determine the type of a rest binding element + // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`, + // we need to preserve generic types instead of substituting them for constraints } const enum SignatureCheckMode { @@ -8456,9 +8459,12 @@ namespace ts { // Return the type of a binding element parent. We check SymbolLinks first to see if a type has been // assigned by contextual typing. - function getTypeForBindingElementParent(node: BindingElementGrandparent) { + function getTypeForBindingElementParent(node: BindingElementGrandparent, checkMode: CheckMode) { + if (checkMode !== CheckMode.Normal) { + return getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false, checkMode); + } const symbol = getSymbolOfNode(node); - return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false); + return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false, checkMode); } function getRestType(source: Type, properties: PropertyName[], symbol: Symbol | undefined): Type { @@ -8589,7 +8595,8 @@ namespace ts { /** Return the inferred type for a binding element */ function getTypeForBindingElement(declaration: BindingElement): Type | undefined { - const parentType = getTypeForBindingElementParent(declaration.parent.parent); + const checkMode = declaration.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal; + const parentType = getTypeForBindingElementParent(declaration.parent.parent, checkMode); return parentType && getBindingElementTypeFromParentType(declaration, parentType); } @@ -8662,9 +8669,9 @@ namespace ts { if (getEffectiveTypeAnnotationNode(walkUpBindingElementsAndPatterns(declaration))) { // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. - return strictNullChecks && !(getFalsyFlags(checkDeclarationInitializer(declaration)) & TypeFlags.Undefined) ? getNonUndefinedType(type) : type; + return strictNullChecks && !(getFalsyFlags(checkDeclarationInitializer(declaration, CheckMode.Normal)) & TypeFlags.Undefined) ? getNonUndefinedType(type) : type; } - return widenTypeInferredFromInitializer(declaration, getUnionType([getNonUndefinedType(type), checkDeclarationInitializer(declaration)], UnionReduction.Subtype)); + return widenTypeInferredFromInitializer(declaration, getUnionType([getNonUndefinedType(type), checkDeclarationInitializer(declaration, CheckMode.Normal)], UnionReduction.Subtype)); } function getTypeForDeclarationFromJSDocComment(declaration: Node) { @@ -8690,11 +8697,15 @@ namespace ts { } // Return the inferred type for a variable, parameter, or property declaration - function getTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag, includeOptionality: boolean): Type | undefined { + function getTypeForVariableLikeDeclaration( + declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag, + includeOptionality: boolean, + checkMode: CheckMode, + ): Type | undefined { // A variable declared in a for..in statement is of type string, or of type keyof T when the // right hand expression is of a type parameter type. if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) { - const indexType = getIndexType(getNonNullableTypeIfNeeded(checkExpression(declaration.parent.parent.expression))); + const indexType = getIndexType(getNonNullableTypeIfNeeded(checkExpression(declaration.parent.parent.expression, /*checkMode*/ checkMode))); return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? getExtractStringType(indexType) : stringType; } @@ -8779,7 +8790,7 @@ namespace ts { return containerObjectType; } } - const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration)); + const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration, checkMode)); return addOptionality(type, isProperty, isOptional); } @@ -9172,7 +9183,7 @@ namespace ts { // contextual type or, if the element itself is a binding pattern, with the type implied by that binding // pattern. const contextualType = isBindingPattern(element.name) ? getTypeFromBindingPattern(element.name, /*includePatternInType*/ true, /*reportErrors*/ false) : unknownType; - return addOptionality(widenTypeInferredFromInitializer(element, checkDeclarationInitializer(element, contextualType))); + return addOptionality(widenTypeInferredFromInitializer(element, checkDeclarationInitializer(element, CheckMode.Normal, contextualType))); } if (isBindingPattern(element.name)) { return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors); @@ -9264,7 +9275,7 @@ namespace ts { // binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the // tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string. function getWidenedTypeForVariableLikeDeclaration(declaration: ParameterDeclaration | PropertyDeclaration | PropertySignature | VariableDeclaration | BindingElement | JSDocPropertyLikeTag, reportErrors?: boolean): Type { - return widenTypeForVariableLikeDeclaration(getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true), declaration, reportErrors); + return widenTypeForVariableLikeDeclaration(getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true, CheckMode.Normal), declaration, reportErrors); } function isGlobalSymbolConstructor(node: Node) { @@ -24965,12 +24976,16 @@ namespace ts { return !!(type.flags & TypeFlags.Instantiable && !maybeTypeOfKind(getBaseConstraintOrType(type), TypeFlags.Nullable)); } - function hasNonBindingPatternContextualTypeWithNoGenericTypes(node: Node) { + function hasContextualTypeWithNoGenericTypes(node: Node, checkMode: CheckMode | undefined) { // Computing the contextual type for a child of a JSX element involves resolving the type of the // element's tag name, so we exclude that here to avoid circularities. + // If check mode has `CheckMode.RestBindingElement`, we skip binding pattern contextual types, + // as we want the type of a rest element to be generic when possible. const contextualType = (isIdentifier(node) || isPropertyAccessExpression(node) || isElementAccessExpression(node)) && !((isJsxOpeningElement(node.parent) || isJsxSelfClosingElement(node.parent)) && node.parent.tagName === node) && - getContextualType(node, ContextFlags.SkipBindingPatterns); + (checkMode && checkMode & CheckMode.RestBindingElement ? + getContextualType(node, ContextFlags.SkipBindingPatterns) + : getContextualType(node)); return contextualType && !isGenericType(contextualType); } @@ -24984,7 +24999,7 @@ namespace ts { // 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'. const substituteConstraints = !(checkMode && checkMode & CheckMode.Inferential) && someType(type, isGenericTypeWithUnionConstraint) && - (isConstraintPosition(type, reference) || hasNonBindingPatternContextualTypeWithNoGenericTypes(reference)); + (isConstraintPosition(type, reference) || hasContextualTypeWithNoGenericTypes(reference, checkMode)); return substituteConstraints ? mapType(type, t => t.flags & TypeFlags.Instantiable && !isMappedTypeGenericIndexedAccess(t) ? getBaseConstraintOrType(t) : t) : type; } @@ -25056,7 +25071,7 @@ namespace ts { const links = getNodeLinks(location); if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) { links.flags |= NodeCheckFlags.InCheckIdentifier; - const parentType = getTypeForBindingElementParent(parent); + const parentType = getTypeForBindingElementParent(parent, CheckMode.Normal); links.flags &= ~NodeCheckFlags.InCheckIdentifier; if (parentType && parentType.flags & TypeFlags.Union && !(parent.kind === SyntaxKind.Parameter && isSymbolAssigned(symbol))) { const pattern = declaration.parent; @@ -25989,7 +26004,7 @@ namespace ts { const parent = declaration.parent.parent; const name = declaration.propertyName || declaration.name; const parentType = getContextualTypeForVariableLikeDeclaration(parent) || - parent.kind !== SyntaxKind.BindingElement && parent.initializer && checkDeclarationInitializer(parent); + parent.kind !== SyntaxKind.BindingElement && parent.initializer && checkDeclarationInitializer(parent, declaration.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal); if (!parentType || isBindingPattern(name) || isComputedNonLiteralName(name)) return undefined; if (parent.name.kind === SyntaxKind.ArrayBindingPattern) { const index = indexOfNode(declaration.parent.elements, declaration); @@ -31768,7 +31783,7 @@ namespace ts { const links = getSymbolLinks(parameter); if (!links.type) { const declaration = parameter.valueDeclaration as ParameterDeclaration; - links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true); + links.type = type || getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true); if (declaration.name.kind !== SyntaxKind.Identifier) { // if inference didn't come up with anything but unknown, fall back to the binding pattern if present. if (links.type === unknownType) { @@ -33685,11 +33700,11 @@ namespace ts { } function checkExpressionCached(node: Expression | QualifiedName, checkMode?: CheckMode): Type { + if (checkMode && checkMode !== CheckMode.Normal) { + return checkExpression(node, checkMode); + } const links = getNodeLinks(node); if (!links.resolvedType) { - if (checkMode && checkMode !== CheckMode.Normal) { - return checkExpression(node, checkMode); - } // When computing a type that we're going to cache, we need to ignore any ongoing control flow // analysis because variables may have transient types in indeterminable states. Moving flowLoopStart // to the top of the stack ensures all transient types are computed from a known point. @@ -33711,10 +33726,16 @@ namespace ts { isJSDocTypeAssertion(node); } - function checkDeclarationInitializer(declaration: HasExpressionInitializer, contextualType?: Type | undefined) { + function checkDeclarationInitializer( + declaration: HasExpressionInitializer, + checkMode: CheckMode, + contextualType?: Type | undefined + ) { const initializer = getEffectiveInitializer(declaration)!; const type = getQuickTypeOfExpression(initializer) || - (contextualType ? checkExpressionWithContextualType(initializer, contextualType, /*inferenceContext*/ undefined, CheckMode.Normal) : checkExpressionCached(initializer)); + (contextualType ? + checkExpressionWithContextualType(initializer, contextualType, /*inferenceContext*/ undefined, checkMode || CheckMode.Normal) + : checkExpressionCached(initializer, checkMode)); return isParameter(declaration) && declaration.name.kind === SyntaxKind.ArrayBindingPattern && isTupleType(type) && !type.target.hasRestElement && getTypeReferenceArity(type) < declaration.name.elements.length ? padTupleType(type, declaration.name) : type; @@ -36974,7 +36995,8 @@ namespace ts { // check private/protected variable access const parent = node.parent.parent; - const parentType = getTypeForBindingElementParent(parent); + const parentCheckMode = node.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal; + const parentType = getTypeForBindingElementParent(parent, parentCheckMode); const name = node.propertyName || node.name; if (parentType && !isBindingPattern(name)) { const exprType = getLiteralTypeFromPropertyName(name); @@ -38372,7 +38394,7 @@ namespace ts { const declaration = catchClause.variableDeclaration; const typeNode = getEffectiveTypeAnnotationNode(getRootDeclaration(declaration)); if (typeNode) { - const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false); + const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false, CheckMode.Normal); if (type && !(type.flags & TypeFlags.AnyOrUnknown)) { grammarErrorOnFirstToken(typeNode, Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified); } @@ -41361,7 +41383,7 @@ namespace ts { } if (isBindingPattern(node)) { - return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true) || errorType; + return getTypeForVariableLikeDeclaration(node.parent, /*includeOptionality*/ true, CheckMode.Normal) || errorType; } if (isInRightSideOfImportOrExportAssignment(node as Identifier)) { diff --git a/tests/baselines/reference/genericObjectSpreadResultInSwitch.types b/tests/baselines/reference/genericObjectSpreadResultInSwitch.types index 33f485c465bd3..4c9ca429daa37 100644 --- a/tests/baselines/reference/genericObjectSpreadResultInSwitch.types +++ b/tests/baselines/reference/genericObjectSpreadResultInSwitch.types @@ -25,7 +25,7 @@ const getType =

(params: P) => { >rest : Omit } = params; ->params : P +>params : Params return rest; >rest : Omit diff --git a/tests/baselines/reference/narrowingDestructuring.js b/tests/baselines/reference/narrowingDestructuring.js new file mode 100644 index 0000000000000..4bc052b7fa0f4 --- /dev/null +++ b/tests/baselines/reference/narrowingDestructuring.js @@ -0,0 +1,89 @@ +//// [narrowingDestructuring.ts] +type X = { kind: "a", a: string } | { kind: "b", b: string } + +function func(value: T) { + if (value.kind === "a") { + value.a; + const { a } = value; + } else { + value.b; + const { b } = value; + } +} + +type Z = { kind: "f", f: { a: number, b: string, c: number } } + | { kind: "g", g: { a: string, b: number, c: string }}; + +function func2(value: T) { + if (value.kind === "f") { + const { f: f1 } = value; + const { f: { a, ...spread } } = value; + value.f; + } else { + const { g: { c, ...spread } } = value; + value.g; + } +} + +function func3(t: T) { + if (t.kind === "a") { + const { kind, ...r1 } = t; + const r2 = (({ kind, ...rest }) => rest)(t); + } +} + +function farr(x: T) { + const [head, ...tail] = x; + if (x[0] === 'number') { + const [head, ...tail] = x; + } +} + +//// [narrowingDestructuring.js] +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +function func(value) { + if (value.kind === "a") { + value.a; + var a = value.a; + } + else { + value.b; + var b = value.b; + } +} +function func2(value) { + if (value.kind === "f") { + var f1 = value.f; + var _a = value.f, a = _a.a, spread = __rest(_a, ["a"]); + value.f; + } + else { + var _b = value.g, c = _b.c, spread = __rest(_b, ["c"]); + value.g; + } +} +function func3(t) { + if (t.kind === "a") { + var kind = t.kind, r1 = __rest(t, ["kind"]); + var r2 = (function (_a) { + var kind = _a.kind, rest = __rest(_a, ["kind"]); + return rest; + })(t); + } +} +function farr(x) { + var head = x[0], tail = x.slice(1); + if (x[0] === 'number') { + var head_1 = x[0], tail_1 = x.slice(1); + } +} diff --git a/tests/baselines/reference/narrowingDestructuring.symbols b/tests/baselines/reference/narrowingDestructuring.symbols new file mode 100644 index 0000000000000..9e7b65997d6a2 --- /dev/null +++ b/tests/baselines/reference/narrowingDestructuring.symbols @@ -0,0 +1,148 @@ +=== tests/cases/compiler/narrowingDestructuring.ts === +type X = { kind: "a", a: string } | { kind: "b", b: string } +>X : Symbol(X, Decl(narrowingDestructuring.ts, 0, 0)) +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 0, 10)) +>a : Symbol(a, Decl(narrowingDestructuring.ts, 0, 21)) +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 0, 37)) +>b : Symbol(b, Decl(narrowingDestructuring.ts, 0, 48)) + +function func(value: T) { +>func : Symbol(func, Decl(narrowingDestructuring.ts, 0, 60)) +>T : Symbol(T, Decl(narrowingDestructuring.ts, 2, 14)) +>X : Symbol(X, Decl(narrowingDestructuring.ts, 0, 0)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 2, 27)) +>T : Symbol(T, Decl(narrowingDestructuring.ts, 2, 14)) + + if (value.kind === "a") { +>value.kind : Symbol(kind, Decl(narrowingDestructuring.ts, 0, 10), Decl(narrowingDestructuring.ts, 0, 37)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 2, 27)) +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 0, 10), Decl(narrowingDestructuring.ts, 0, 37)) + + value.a; +>value.a : Symbol(a, Decl(narrowingDestructuring.ts, 0, 21)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 2, 27)) +>a : Symbol(a, Decl(narrowingDestructuring.ts, 0, 21)) + + const { a } = value; +>a : Symbol(a, Decl(narrowingDestructuring.ts, 5, 15)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 2, 27)) + + } else { + value.b; +>value.b : Symbol(b, Decl(narrowingDestructuring.ts, 0, 48)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 2, 27)) +>b : Symbol(b, Decl(narrowingDestructuring.ts, 0, 48)) + + const { b } = value; +>b : Symbol(b, Decl(narrowingDestructuring.ts, 8, 15)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 2, 27)) + } +} + +type Z = { kind: "f", f: { a: number, b: string, c: number } } +>Z : Symbol(Z, Decl(narrowingDestructuring.ts, 10, 1)) +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 12, 10)) +>f : Symbol(f, Decl(narrowingDestructuring.ts, 12, 21)) +>a : Symbol(a, Decl(narrowingDestructuring.ts, 12, 26)) +>b : Symbol(b, Decl(narrowingDestructuring.ts, 12, 37)) +>c : Symbol(c, Decl(narrowingDestructuring.ts, 12, 48)) + + | { kind: "g", g: { a: string, b: number, c: string }}; +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 13, 7)) +>g : Symbol(g, Decl(narrowingDestructuring.ts, 13, 18)) +>a : Symbol(a, Decl(narrowingDestructuring.ts, 13, 23)) +>b : Symbol(b, Decl(narrowingDestructuring.ts, 13, 34)) +>c : Symbol(c, Decl(narrowingDestructuring.ts, 13, 45)) + +function func2(value: T) { +>func2 : Symbol(func2, Decl(narrowingDestructuring.ts, 13, 59)) +>T : Symbol(T, Decl(narrowingDestructuring.ts, 15, 15)) +>Z : Symbol(Z, Decl(narrowingDestructuring.ts, 10, 1)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 15, 28)) +>T : Symbol(T, Decl(narrowingDestructuring.ts, 15, 15)) + + if (value.kind === "f") { +>value.kind : Symbol(kind, Decl(narrowingDestructuring.ts, 12, 10), Decl(narrowingDestructuring.ts, 13, 7)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 15, 28)) +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 12, 10), Decl(narrowingDestructuring.ts, 13, 7)) + + const { f: f1 } = value; +>f : Symbol(f, Decl(narrowingDestructuring.ts, 12, 21)) +>f1 : Symbol(f1, Decl(narrowingDestructuring.ts, 17, 15)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 15, 28)) + + const { f: { a, ...spread } } = value; +>f : Symbol(f, Decl(narrowingDestructuring.ts, 12, 21)) +>a : Symbol(a, Decl(narrowingDestructuring.ts, 18, 20)) +>spread : Symbol(spread, Decl(narrowingDestructuring.ts, 18, 23)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 15, 28)) + + value.f; +>value.f : Symbol(f, Decl(narrowingDestructuring.ts, 12, 21)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 15, 28)) +>f : Symbol(f, Decl(narrowingDestructuring.ts, 12, 21)) + + } else { + const { g: { c, ...spread } } = value; +>g : Symbol(g, Decl(narrowingDestructuring.ts, 13, 18)) +>c : Symbol(c, Decl(narrowingDestructuring.ts, 21, 20)) +>spread : Symbol(spread, Decl(narrowingDestructuring.ts, 21, 23)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 15, 28)) + + value.g; +>value.g : Symbol(g, Decl(narrowingDestructuring.ts, 13, 18)) +>value : Symbol(value, Decl(narrowingDestructuring.ts, 15, 28)) +>g : Symbol(g, Decl(narrowingDestructuring.ts, 13, 18)) + } +} + +function func3(t: T) { +>func3 : Symbol(func3, Decl(narrowingDestructuring.ts, 24, 1)) +>T : Symbol(T, Decl(narrowingDestructuring.ts, 26, 15)) +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 26, 26)) +>a : Symbol(a, Decl(narrowingDestructuring.ts, 26, 37)) +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 26, 53)) +>b : Symbol(b, Decl(narrowingDestructuring.ts, 26, 64)) +>t : Symbol(t, Decl(narrowingDestructuring.ts, 26, 78)) +>T : Symbol(T, Decl(narrowingDestructuring.ts, 26, 15)) + + if (t.kind === "a") { +>t.kind : Symbol(kind, Decl(narrowingDestructuring.ts, 26, 26), Decl(narrowingDestructuring.ts, 26, 53)) +>t : Symbol(t, Decl(narrowingDestructuring.ts, 26, 78)) +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 26, 26), Decl(narrowingDestructuring.ts, 26, 53)) + + const { kind, ...r1 } = t; +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 28, 15)) +>r1 : Symbol(r1, Decl(narrowingDestructuring.ts, 28, 21)) +>t : Symbol(t, Decl(narrowingDestructuring.ts, 26, 78)) + + const r2 = (({ kind, ...rest }) => rest)(t); +>r2 : Symbol(r2, Decl(narrowingDestructuring.ts, 29, 13)) +>kind : Symbol(kind, Decl(narrowingDestructuring.ts, 29, 22)) +>rest : Symbol(rest, Decl(narrowingDestructuring.ts, 29, 28)) +>rest : Symbol(rest, Decl(narrowingDestructuring.ts, 29, 28)) +>t : Symbol(t, Decl(narrowingDestructuring.ts, 26, 78)) + } +} + +function farr(x: T) { +>farr : Symbol(farr, Decl(narrowingDestructuring.ts, 31, 1)) +>T : Symbol(T, Decl(narrowingDestructuring.ts, 33, 14)) +>x : Symbol(x, Decl(narrowingDestructuring.ts, 33, 77)) +>T : Symbol(T, Decl(narrowingDestructuring.ts, 33, 14)) + + const [head, ...tail] = x; +>head : Symbol(head, Decl(narrowingDestructuring.ts, 34, 11)) +>tail : Symbol(tail, Decl(narrowingDestructuring.ts, 34, 16)) +>x : Symbol(x, Decl(narrowingDestructuring.ts, 33, 77)) + + if (x[0] === 'number') { +>x : Symbol(x, Decl(narrowingDestructuring.ts, 33, 77)) +>0 : Symbol(0) + + const [head, ...tail] = x; +>head : Symbol(head, Decl(narrowingDestructuring.ts, 36, 15)) +>tail : Symbol(tail, Decl(narrowingDestructuring.ts, 36, 20)) +>x : Symbol(x, Decl(narrowingDestructuring.ts, 33, 77)) + } +} diff --git a/tests/baselines/reference/narrowingDestructuring.types b/tests/baselines/reference/narrowingDestructuring.types new file mode 100644 index 0000000000000..a942e163da860 --- /dev/null +++ b/tests/baselines/reference/narrowingDestructuring.types @@ -0,0 +1,150 @@ +=== tests/cases/compiler/narrowingDestructuring.ts === +type X = { kind: "a", a: string } | { kind: "b", b: string } +>X : X +>kind : "a" +>a : string +>kind : "b" +>b : string + +function func(value: T) { +>func : (value: T) => void +>value : T + + if (value.kind === "a") { +>value.kind === "a" : boolean +>value.kind : "a" | "b" +>value : X +>kind : "a" | "b" +>"a" : "a" + + value.a; +>value.a : string +>value : { kind: "a"; a: string; } +>a : string + + const { a } = value; +>a : string +>value : { kind: "a"; a: string; } + + } else { + value.b; +>value.b : string +>value : { kind: "b"; b: string; } +>b : string + + const { b } = value; +>b : string +>value : { kind: "b"; b: string; } + } +} + +type Z = { kind: "f", f: { a: number, b: string, c: number } } +>Z : Z +>kind : "f" +>f : { a: number; b: string; c: number; } +>a : number +>b : string +>c : number + + | { kind: "g", g: { a: string, b: number, c: string }}; +>kind : "g" +>g : { a: string; b: number; c: string; } +>a : string +>b : number +>c : string + +function func2(value: T) { +>func2 : (value: T) => void +>value : T + + if (value.kind === "f") { +>value.kind === "f" : boolean +>value.kind : "f" | "g" +>value : Z +>kind : "f" | "g" +>"f" : "f" + + const { f: f1 } = value; +>f : any +>f1 : { a: number; b: string; c: number; } +>value : { kind: "f"; f: { a: number; b: string; c: number; }; } + + const { f: { a, ...spread } } = value; +>f : any +>a : number +>spread : { b: string; c: number; } +>value : { kind: "f"; f: { a: number; b: string; c: number; }; } + + value.f; +>value.f : { a: number; b: string; c: number; } +>value : { kind: "f"; f: { a: number; b: string; c: number; }; } +>f : { a: number; b: string; c: number; } + + } else { + const { g: { c, ...spread } } = value; +>g : any +>c : string +>spread : { a: string; b: number; } +>value : { kind: "g"; g: { a: string; b: number; c: string; }; } + + value.g; +>value.g : { a: string; b: number; c: string; } +>value : { kind: "g"; g: { a: string; b: number; c: string; }; } +>g : { a: string; b: number; c: string; } + } +} + +function func3(t: T) { +>func3 : (t: T) => void +>kind : "a" +>a : string +>kind : "b" +>b : number +>t : T + + if (t.kind === "a") { +>t.kind === "a" : boolean +>t.kind : "a" | "b" +>t : { kind: "a"; a: string; } | { kind: "b"; b: number; } +>kind : "a" | "b" +>"a" : "a" + + const { kind, ...r1 } = t; +>kind : "a" +>r1 : Omit +>t : { kind: "a"; a: string; } + + const r2 = (({ kind, ...rest }) => rest)(t); +>r2 : { a: string; } +>(({ kind, ...rest }) => rest)(t) : { a: string; } +>(({ kind, ...rest }) => rest) : ({ kind, ...rest }: { kind: "a"; a: string; }) => { a: string; } +>({ kind, ...rest }) => rest : ({ kind, ...rest }: { kind: "a"; a: string; }) => { a: string; } +>kind : "a" +>rest : { a: string; } +>rest : { a: string; } +>t : { kind: "a"; a: string; } + } +} + +function farr(x: T) { +>farr : (x: T) => void +>x : T + + const [head, ...tail] = x; +>head : string | number +>tail : (string | number)[] +>x : [number, string, string] | [string, number, number] + + if (x[0] === 'number') { +>x[0] === 'number' : boolean +>x[0] : string | number +>x : [number, string, string] | [string, number, number] +>0 : 0 +>'number' : "number" + + const [head, ...tail] = x; +>head : "number" +>tail : (string | number)[] +>x : [number, string, string] | [string, number, number] + } +} diff --git a/tests/baselines/reference/restInvalidArgumentType.types b/tests/baselines/reference/restInvalidArgumentType.types index 0e44cdfe95ccb..119b0394726da 100644 --- a/tests/baselines/reference/restInvalidArgumentType.types +++ b/tests/baselines/reference/restInvalidArgumentType.types @@ -84,7 +84,7 @@ function f(p1: T, p2: T[]) { var {...r5} = k; // Error, index >r5 : any ->k : keyof T +>k : string | number | symbol var {...r6} = mapped_generic; // Error, generic mapped object type >r6 : { [P in keyof T]: T[P]; } diff --git a/tests/cases/compiler/narrowingDestructuring.ts b/tests/cases/compiler/narrowingDestructuring.ts new file mode 100644 index 0000000000000..a745802825945 --- /dev/null +++ b/tests/cases/compiler/narrowingDestructuring.ts @@ -0,0 +1,39 @@ +type X = { kind: "a", a: string } | { kind: "b", b: string } + +function func(value: T) { + if (value.kind === "a") { + value.a; + const { a } = value; + } else { + value.b; + const { b } = value; + } +} + +type Z = { kind: "f", f: { a: number, b: string, c: number } } + | { kind: "g", g: { a: string, b: number, c: string }}; + +function func2(value: T) { + if (value.kind === "f") { + const { f: f1 } = value; + const { f: { a, ...spread } } = value; + value.f; + } else { + const { g: { c, ...spread } } = value; + value.g; + } +} + +function func3(t: T) { + if (t.kind === "a") { + const { kind, ...r1 } = t; + const r2 = (({ kind, ...rest }) => rest)(t); + } +} + +function farr(x: T) { + const [head, ...tail] = x; + if (x[0] === 'number') { + const [head, ...tail] = x; + } +} \ No newline at end of file