mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Fix destructuring and narrowing interaction (#47337)
* WIP: pass in checkmode to getNarrowableTypeForReference * add tests * another pass through new check mode argument * rename new check mode * only use rest flag for rest elements in objects * add and update tests * change check mode flag name * restore package-lock.json * fix comments * get rid of fourslash tests * fix caching in checkExpressionCached when checkMode is not normal * Don't distinguish between object and array rest elements * get rid of undefined check mode * don't make includeOptionality into checkmode flag
This commit is contained in:
+52
-30
@@ -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 {
|
||||
@@ -8461,9 +8464,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 {
|
||||
@@ -8594,7 +8600,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);
|
||||
}
|
||||
|
||||
@@ -8667,9 +8674,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) {
|
||||
@@ -8695,11 +8702,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;
|
||||
}
|
||||
|
||||
@@ -8784,7 +8795,7 @@ namespace ts {
|
||||
return containerObjectType;
|
||||
}
|
||||
}
|
||||
const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration));
|
||||
const type = widenTypeInferredFromInitializer(declaration, checkDeclarationInitializer(declaration, checkMode));
|
||||
return addOptionality(type, isProperty, isOptional);
|
||||
}
|
||||
|
||||
@@ -9177,7 +9188,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);
|
||||
@@ -9269,7 +9280,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) {
|
||||
@@ -25019,12 +25030,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);
|
||||
}
|
||||
|
||||
@@ -25038,7 +25053,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;
|
||||
}
|
||||
|
||||
@@ -25110,7 +25125,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;
|
||||
@@ -26047,7 +26062,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);
|
||||
@@ -31807,7 +31822,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) {
|
||||
@@ -33724,11 +33739,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.
|
||||
@@ -33750,10 +33765,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;
|
||||
@@ -37011,7 +37032,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);
|
||||
@@ -38409,7 +38431,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);
|
||||
}
|
||||
@@ -41440,7 +41462,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)) {
|
||||
|
||||
@@ -25,7 +25,7 @@ const getType = <P extends Params>(params: P) => {
|
||||
>rest : Omit<P, "foo">
|
||||
|
||||
} = params;
|
||||
>params : P
|
||||
>params : Params
|
||||
|
||||
return rest;
|
||||
>rest : Omit<P, "foo">
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
//// [narrowingDestructuring.ts]
|
||||
type X = { kind: "a", a: string } | { kind: "b", b: string }
|
||||
|
||||
function func<T extends X>(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<T extends Z>(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 extends { kind: "a", a: string } | { kind: "b", b: number }>(t: T) {
|
||||
if (t.kind === "a") {
|
||||
const { kind, ...r1 } = t;
|
||||
const r2 = (({ kind, ...rest }) => rest)(t);
|
||||
}
|
||||
}
|
||||
|
||||
function farr<T extends [number, string, string] | [string, number, number]>(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);
|
||||
}
|
||||
}
|
||||
@@ -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<T extends X>(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<T extends Z>(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 extends { kind: "a", a: string } | { kind: "b", b: number }>(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<T extends [number, string, string] | [string, number, number]>(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))
|
||||
}
|
||||
}
|
||||
@@ -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<T extends X>(value: T) {
|
||||
>func : <T extends X>(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<T extends Z>(value: T) {
|
||||
>func2 : <T extends Z>(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 extends { kind: "a", a: string } | { kind: "b", b: number }>(t: T) {
|
||||
>func3 : <T extends { kind: "a"; a: string; } | { kind: "b"; b: number; }>(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">
|
||||
>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<T extends [number, string, string] | [string, number, number]>(x: T) {
|
||||
>farr : <T extends [number, string, string] | [string, number, number]>(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]
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ function f<T extends { b: string }>(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]; }
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
type X = { kind: "a", a: string } | { kind: "b", b: string }
|
||||
|
||||
function func<T extends X>(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<T extends Z>(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 extends { kind: "a", a: string } | { kind: "b", b: number }>(t: T) {
|
||||
if (t.kind === "a") {
|
||||
const { kind, ...r1 } = t;
|
||||
const r2 = (({ kind, ...rest }) => rest)(t);
|
||||
}
|
||||
}
|
||||
|
||||
function farr<T extends [number, string, string] | [string, number, number]>(x: T) {
|
||||
const [head, ...tail] = x;
|
||||
if (x[0] === 'number') {
|
||||
const [head, ...tail] = x;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user