diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1c828ff057ae8..8c0f15874e03f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9785,14 +9785,11 @@ namespace ts { if (checkType === wildcardType || extendsType === wildcardType) { return wildcardType; } - // If this is a distributive conditional type and the check type is generic we need to defer - // resolution of the conditional type such that a later instantiation will properly distribute - // over union types. - const isDeferred = root.isDistributive && maybeTypeOfKind(checkType, TypeFlags.Instantiable); + const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable); let combinedMapper: TypeMapper | undefined; if (root.inferTypeParameters) { const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None); - if (!isDeferred) { + if (!checkTypeInstantiable) { // We don't want inferences from constraints as they may cause us to eagerly resolve the // conditional type instead of deferring resolution. Also, we always want strict function // types rules (i.e. proper contravariance) for inferences. @@ -9800,16 +9797,17 @@ namespace ts { } combinedMapper = combineTypeMappers(mapper, context); } - if (!isDeferred) { - if (extendsType.flags & TypeFlags.AnyOrUnknown) { + // Instantiate the extends type including inferences for 'infer T' type parameters + const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType; + // We attempt to resolve the conditional type only when the check and extends types are non-generic + if (!checkTypeInstantiable && !maybeTypeOfKind(inferredExtendsType, TypeFlags.Instantiable)) { + if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { return instantiateType(root.trueType, mapper); } // Return union of trueType and falseType for 'any' since it matches anything if (checkType.flags & TypeFlags.Any) { return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]); } - // Instantiate the extends type including inferences for 'infer T' type parameters - const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType; // Return falseType for a definitely false extends check. We check an instantations of the two // types with type parameters mapped to the wildcard type, the most permissive instantiations // possible (the wildcard type is assignable to and from all types). If those are not related, @@ -11837,7 +11835,7 @@ namespace ts { if (!noImplicitAny && getObjectFlags(target) & ObjectFlags.JSLiteral) { return false; // Disable excess property checks on JS literals to simulate having an implicit "index signature" - but only outside of noImplicitAny } - if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { + if (isExcessPropertyCheckTarget(target)) { const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes); if ((relation === assignableRelation || relation === definitelyAssignableRelation || relation === comparableRelation) && (isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) { @@ -11850,6 +11848,9 @@ namespace ts { for (const prop of getPropertiesOfObjectType(source)) { if (shouldCheckAsExcessProperty(prop, source.symbol) && !isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { if (reportErrors) { + // Report error in terms of object types in the target as those are the only ones + // we check in isKnownProperty. + const errorTarget = filterType(target, isExcessPropertyCheckTarget); // We know *exactly* where things went wrong when comparing the types. // Use this property as the error node as this will be more helpful in // reasoning about what went wrong. @@ -11858,7 +11859,7 @@ namespace ts { // JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal. // However, using an object-literal error message will be very confusing to the users so we give different a message. // TODO: Spelling suggestions for excess jsx attributes (needs new diagnostic messages) - reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(prop), typeToString(target)); + reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(prop), typeToString(errorTarget)); } else { // use the property's value declaration if the property is assigned inside the literal itself @@ -11872,17 +11873,17 @@ namespace ts { const name = propDeclaration.name!; if (isIdentifier(name)) { - suggestion = getSuggestionForNonexistentProperty(name, target); + suggestion = getSuggestionForNonexistentProperty(name, errorTarget); } } if (suggestion !== undefined) { reportError(Diagnostics.Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2, - symbolToString(prop), typeToString(target), suggestion); + symbolToString(prop), typeToString(errorTarget), suggestion); } else { reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, - symbolToString(prop), typeToString(target)); + symbolToString(prop), typeToString(errorTarget)); } } } @@ -14615,15 +14616,15 @@ namespace ts { getAccessedPropertyName(source as PropertyAccessExpression | ElementAccessExpression) === getAccessedPropertyName(target) && isMatchingReference((source as PropertyAccessExpression | ElementAccessExpression).expression, target.expression); case SyntaxKind.BindingElement: - if (target.kind !== SyntaxKind.PropertyAccessExpression) return false; - const t = target as PropertyAccessExpression; - if (t.name.escapedText !== getBindingElementNameText(source as BindingElement)) return false; - if (source.parent.parent.kind === SyntaxKind.BindingElement && isMatchingReference(source.parent.parent, t.expression)) { - return true; - } - if (source.parent.parent.kind === SyntaxKind.VariableDeclaration) { - const maybeId = (source.parent.parent as VariableDeclaration).initializer; - return !!maybeId && isMatchingReference(maybeId, t.expression); + if (target.kind === SyntaxKind.PropertyAccessExpression && (target).name.escapedText === getBindingElementNameText(source)) { + const ancestor = source.parent.parent; + if (ancestor.kind === SyntaxKind.BindingElement) { + return isMatchingReference(ancestor, (target).expression); + } + if (ancestor.kind === SyntaxKind.VariableDeclaration) { + const initializer = (ancestor).initializer; + return !!initializer && isMatchingReference(initializer, (target).expression); + } } } return false; @@ -14635,14 +14636,25 @@ namespace ts { undefined; } + function getReferenceParent(source: Node) { + if (source.kind === SyntaxKind.PropertyAccessExpression) { + return (source).expression; + } + if (source.kind === SyntaxKind.BindingElement) { + const ancestor = source.parent.parent; + return ancestor.kind === SyntaxKind.VariableDeclaration ? (ancestor).initializer : ancestor; + } + return undefined; + } + function containsMatchingReference(source: Node, target: Node) { - while (source.kind === SyntaxKind.PropertyAccessExpression) { - source = (source).expression; - if (isMatchingReference(source, target)) { + let parent = getReferenceParent(source); + while (parent) { + if (isMatchingReference(parent, target)) { return true; } + parent = getReferenceParent(parent); } - return false; } // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared @@ -18584,20 +18596,23 @@ namespace ts { return true; } } - else if (targetType.flags & TypeFlags.UnionOrIntersection) { + else if (targetType.flags & TypeFlags.UnionOrIntersection && isExcessPropertyCheckTarget(targetType)) { for (const t of (targetType as UnionOrIntersectionType).types) { if (isKnownProperty(t, name, isComparingJsxAttributes)) { return true; } } } - else if (targetType.flags & TypeFlags.Conditional) { - return isKnownProperty((targetType as ConditionalType).root.trueType, name, isComparingJsxAttributes) || - isKnownProperty((targetType as ConditionalType).root.falseType, name, isComparingJsxAttributes); - } return false; } + function isExcessPropertyCheckTarget(type: Type): boolean { + return !!(type.flags & TypeFlags.Object && !(getObjectFlags(type) & ObjectFlags.ObjectLiteralPatternWithComputedProperties) || + type.flags & TypeFlags.NonPrimitive || + type.flags & TypeFlags.Union && some((type).types, isExcessPropertyCheckTarget) || + type.flags & TypeFlags.Intersection && every((type).types, isExcessPropertyCheckTarget)); + } + function checkJsxExpression(node: JsxExpression, checkMode?: CheckMode) { if (node.expression) { const type = checkExpression(node.expression, checkMode); diff --git a/tests/baselines/reference/conditionalTypes2.errors.txt b/tests/baselines/reference/conditionalTypes2.errors.txt index fdf210f47932a..a1a23b6da1870 100644 --- a/tests/baselines/reference/conditionalTypes2.errors.txt +++ b/tests/baselines/reference/conditionalTypes2.errors.txt @@ -203,4 +203,47 @@ tests/cases/conformance/types/conditional/conditionalTypes2.ts(75,12): error TS2 type C2 = T extends object ? { [Q in keyof T]: C2; } : T; + + // Repro from #28654 + + type MaybeTrue = true extends T["b"] ? "yes" : "no"; + + type T0 = MaybeTrue<{ b: never }> // "no" + type T1 = MaybeTrue<{ b: false }>; // "no" + type T2 = MaybeTrue<{ b: true }>; // "yes" + type T3 = MaybeTrue<{ b: boolean }>; // "yes" + + // Repro from #28824 + + type Union = 'a' | 'b'; + type Product = { f1: A, f2: B}; + type ProductUnion = Product<'a', 0> | Product<'b', 1>; + + // {a: "b"; b: "a"} + type UnionComplement = { + [K in Union]: Exclude + }; + type UCA = UnionComplement['a']; + type UCB = UnionComplement['b']; + + // {a: "a"; b: "b"} + type UnionComplementComplement = { + [K in Union]: Exclude> + }; + type UCCA = UnionComplementComplement['a']; + type UCCB = UnionComplementComplement['b']; + + // {a: Product<'b', 1>; b: Product<'a', 0>} + type ProductComplement = { + [K in Union]: Exclude + }; + type PCA = ProductComplement['a']; + type PCB = ProductComplement['b']; + + // {a: Product<'a', 0>; b: Product<'b', 1>} + type ProductComplementComplement = { + [K in Union]: Exclude> + }; + type PCCA = ProductComplementComplement['a']; + type PCCB = ProductComplementComplement['b']; \ No newline at end of file diff --git a/tests/baselines/reference/conditionalTypes2.js b/tests/baselines/reference/conditionalTypes2.js index 1f95e7eb8d9f2..4f4f35e821afb 100644 --- a/tests/baselines/reference/conditionalTypes2.js +++ b/tests/baselines/reference/conditionalTypes2.js @@ -145,6 +145,49 @@ type B2 = type C2 = T extends object ? { [Q in keyof T]: C2; } : T; + +// Repro from #28654 + +type MaybeTrue = true extends T["b"] ? "yes" : "no"; + +type T0 = MaybeTrue<{ b: never }> // "no" +type T1 = MaybeTrue<{ b: false }>; // "no" +type T2 = MaybeTrue<{ b: true }>; // "yes" +type T3 = MaybeTrue<{ b: boolean }>; // "yes" + +// Repro from #28824 + +type Union = 'a' | 'b'; +type Product = { f1: A, f2: B}; +type ProductUnion = Product<'a', 0> | Product<'b', 1>; + +// {a: "b"; b: "a"} +type UnionComplement = { + [K in Union]: Exclude +}; +type UCA = UnionComplement['a']; +type UCB = UnionComplement['b']; + +// {a: "a"; b: "b"} +type UnionComplementComplement = { + [K in Union]: Exclude> +}; +type UCCA = UnionComplementComplement['a']; +type UCCB = UnionComplementComplement['b']; + +// {a: Product<'b', 1>; b: Product<'a', 0>} +type ProductComplement = { + [K in Union]: Exclude +}; +type PCA = ProductComplement['a']; +type PCB = ProductComplement['b']; + +// {a: Product<'a', 0>; b: Product<'b', 1>} +type ProductComplementComplement = { + [K in Union]: Exclude> +}; +type PCCA = ProductComplementComplement['a']; +type PCCB = ProductComplementComplement['b']; //// [conditionalTypes2.js] @@ -304,3 +347,48 @@ declare type B2 = T extends object ? T extends any[] ? T : { declare type C2 = T extends object ? { [Q in keyof T]: C2; } : T; +declare type MaybeTrue = true extends T["b"] ? "yes" : "no"; +declare type T0 = MaybeTrue<{ + b: never; +}>; +declare type T1 = MaybeTrue<{ + b: false; +}>; +declare type T2 = MaybeTrue<{ + b: true; +}>; +declare type T3 = MaybeTrue<{ + b: boolean; +}>; +declare type Union = 'a' | 'b'; +declare type Product = { + f1: A; + f2: B; +}; +declare type ProductUnion = Product<'a', 0> | Product<'b', 1>; +declare type UnionComplement = { + [K in Union]: Exclude; +}; +declare type UCA = UnionComplement['a']; +declare type UCB = UnionComplement['b']; +declare type UnionComplementComplement = { + [K in Union]: Exclude>; +}; +declare type UCCA = UnionComplementComplement['a']; +declare type UCCB = UnionComplementComplement['b']; +declare type ProductComplement = { + [K in Union]: Exclude; +}; +declare type PCA = ProductComplement['a']; +declare type PCB = ProductComplement['b']; +declare type ProductComplementComplement = { + [K in Union]: Exclude>; +}; +declare type PCCA = ProductComplementComplement['a']; +declare type PCCB = ProductComplementComplement['b']; diff --git a/tests/baselines/reference/conditionalTypes2.symbols b/tests/baselines/reference/conditionalTypes2.symbols index c52179e81b01c..b164d26e45089 100644 --- a/tests/baselines/reference/conditionalTypes2.symbols +++ b/tests/baselines/reference/conditionalTypes2.symbols @@ -551,3 +551,137 @@ type C2 = >E : Symbol(E, Decl(conditionalTypes2.ts, 144, 13)) >T : Symbol(T, Decl(conditionalTypes2.ts, 144, 8)) +// Repro from #28654 + +type MaybeTrue = true extends T["b"] ? "yes" : "no"; +>MaybeTrue : Symbol(MaybeTrue, Decl(conditionalTypes2.ts, 145, 63)) +>T : Symbol(T, Decl(conditionalTypes2.ts, 149, 15)) +>b : Symbol(b, Decl(conditionalTypes2.ts, 149, 26)) +>T : Symbol(T, Decl(conditionalTypes2.ts, 149, 15)) + +type T0 = MaybeTrue<{ b: never }> // "no" +>T0 : Symbol(T0, Decl(conditionalTypes2.ts, 149, 78)) +>MaybeTrue : Symbol(MaybeTrue, Decl(conditionalTypes2.ts, 145, 63)) +>b : Symbol(b, Decl(conditionalTypes2.ts, 151, 21)) + +type T1 = MaybeTrue<{ b: false }>; // "no" +>T1 : Symbol(T1, Decl(conditionalTypes2.ts, 151, 33)) +>MaybeTrue : Symbol(MaybeTrue, Decl(conditionalTypes2.ts, 145, 63)) +>b : Symbol(b, Decl(conditionalTypes2.ts, 152, 21)) + +type T2 = MaybeTrue<{ b: true }>; // "yes" +>T2 : Symbol(T2, Decl(conditionalTypes2.ts, 152, 34)) +>MaybeTrue : Symbol(MaybeTrue, Decl(conditionalTypes2.ts, 145, 63)) +>b : Symbol(b, Decl(conditionalTypes2.ts, 153, 21)) + +type T3 = MaybeTrue<{ b: boolean }>; // "yes" +>T3 : Symbol(T3, Decl(conditionalTypes2.ts, 153, 33)) +>MaybeTrue : Symbol(MaybeTrue, Decl(conditionalTypes2.ts, 145, 63)) +>b : Symbol(b, Decl(conditionalTypes2.ts, 154, 21)) + +// Repro from #28824 + +type Union = 'a' | 'b'; +>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36)) + +type Product = { f1: A, f2: B}; +>Product : Symbol(Product, Decl(conditionalTypes2.ts, 158, 23)) +>A : Symbol(A, Decl(conditionalTypes2.ts, 159, 13)) +>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36)) +>B : Symbol(B, Decl(conditionalTypes2.ts, 159, 29)) +>f1 : Symbol(f1, Decl(conditionalTypes2.ts, 159, 36)) +>A : Symbol(A, Decl(conditionalTypes2.ts, 159, 13)) +>f2 : Symbol(f2, Decl(conditionalTypes2.ts, 159, 43)) +>B : Symbol(B, Decl(conditionalTypes2.ts, 159, 29)) + +type ProductUnion = Product<'a', 0> | Product<'b', 1>; +>ProductUnion : Symbol(ProductUnion, Decl(conditionalTypes2.ts, 159, 51)) +>Product : Symbol(Product, Decl(conditionalTypes2.ts, 158, 23)) +>Product : Symbol(Product, Decl(conditionalTypes2.ts, 158, 23)) + +// {a: "b"; b: "a"} +type UnionComplement = { +>UnionComplement : Symbol(UnionComplement, Decl(conditionalTypes2.ts, 160, 54)) + + [K in Union]: Exclude +>K : Symbol(K, Decl(conditionalTypes2.ts, 164, 3)) +>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36)) +>K : Symbol(K, Decl(conditionalTypes2.ts, 164, 3)) + +}; +type UCA = UnionComplement['a']; +>UCA : Symbol(UCA, Decl(conditionalTypes2.ts, 165, 2)) +>UnionComplement : Symbol(UnionComplement, Decl(conditionalTypes2.ts, 160, 54)) + +type UCB = UnionComplement['b']; +>UCB : Symbol(UCB, Decl(conditionalTypes2.ts, 166, 32)) +>UnionComplement : Symbol(UnionComplement, Decl(conditionalTypes2.ts, 160, 54)) + +// {a: "a"; b: "b"} +type UnionComplementComplement = { +>UnionComplementComplement : Symbol(UnionComplementComplement, Decl(conditionalTypes2.ts, 167, 32)) + + [K in Union]: Exclude> +>K : Symbol(K, Decl(conditionalTypes2.ts, 171, 3)) +>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36)) +>K : Symbol(K, Decl(conditionalTypes2.ts, 171, 3)) + +}; +type UCCA = UnionComplementComplement['a']; +>UCCA : Symbol(UCCA, Decl(conditionalTypes2.ts, 172, 2)) +>UnionComplementComplement : Symbol(UnionComplementComplement, Decl(conditionalTypes2.ts, 167, 32)) + +type UCCB = UnionComplementComplement['b']; +>UCCB : Symbol(UCCB, Decl(conditionalTypes2.ts, 173, 43)) +>UnionComplementComplement : Symbol(UnionComplementComplement, Decl(conditionalTypes2.ts, 167, 32)) + +// {a: Product<'b', 1>; b: Product<'a', 0>} +type ProductComplement = { +>ProductComplement : Symbol(ProductComplement, Decl(conditionalTypes2.ts, 174, 43)) + + [K in Union]: Exclude +>K : Symbol(K, Decl(conditionalTypes2.ts, 178, 3)) +>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>ProductUnion : Symbol(ProductUnion, Decl(conditionalTypes2.ts, 159, 51)) +>f1 : Symbol(f1, Decl(conditionalTypes2.ts, 178, 39)) +>K : Symbol(K, Decl(conditionalTypes2.ts, 178, 3)) + +}; +type PCA = ProductComplement['a']; +>PCA : Symbol(PCA, Decl(conditionalTypes2.ts, 179, 2)) +>ProductComplement : Symbol(ProductComplement, Decl(conditionalTypes2.ts, 174, 43)) + +type PCB = ProductComplement['b']; +>PCB : Symbol(PCB, Decl(conditionalTypes2.ts, 180, 34)) +>ProductComplement : Symbol(ProductComplement, Decl(conditionalTypes2.ts, 174, 43)) + +// {a: Product<'a', 0>; b: Product<'b', 1>} +type ProductComplementComplement = { +>ProductComplementComplement : Symbol(ProductComplementComplement, Decl(conditionalTypes2.ts, 181, 34)) + + [K in Union]: Exclude> +>K : Symbol(K, Decl(conditionalTypes2.ts, 185, 3)) +>Union : Symbol(Union, Decl(conditionalTypes2.ts, 154, 36)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>ProductUnion : Symbol(ProductUnion, Decl(conditionalTypes2.ts, 159, 51)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>ProductUnion : Symbol(ProductUnion, Decl(conditionalTypes2.ts, 159, 51)) +>f1 : Symbol(f1, Decl(conditionalTypes2.ts, 185, 61)) +>K : Symbol(K, Decl(conditionalTypes2.ts, 185, 3)) + +}; +type PCCA = ProductComplementComplement['a']; +>PCCA : Symbol(PCCA, Decl(conditionalTypes2.ts, 186, 2)) +>ProductComplementComplement : Symbol(ProductComplementComplement, Decl(conditionalTypes2.ts, 181, 34)) + +type PCCB = ProductComplementComplement['b']; +>PCCB : Symbol(PCCB, Decl(conditionalTypes2.ts, 187, 45)) +>ProductComplementComplement : Symbol(ProductComplementComplement, Decl(conditionalTypes2.ts, 181, 34)) + diff --git a/tests/baselines/reference/conditionalTypes2.types b/tests/baselines/reference/conditionalTypes2.types index 58ce7e5d5b49e..aac6ba47475c6 100644 --- a/tests/baselines/reference/conditionalTypes2.types +++ b/tests/baselines/reference/conditionalTypes2.types @@ -341,3 +341,93 @@ type C2 = T extends object ? { [Q in keyof T]: C2; } : T; +// Repro from #28654 + +type MaybeTrue = true extends T["b"] ? "yes" : "no"; +>MaybeTrue : MaybeTrue +>b : boolean +>true : true + +type T0 = MaybeTrue<{ b: never }> // "no" +>T0 : "no" +>b : never + +type T1 = MaybeTrue<{ b: false }>; // "no" +>T1 : "no" +>b : false +>false : false + +type T2 = MaybeTrue<{ b: true }>; // "yes" +>T2 : "yes" +>b : true +>true : true + +type T3 = MaybeTrue<{ b: boolean }>; // "yes" +>T3 : "yes" +>b : boolean + +// Repro from #28824 + +type Union = 'a' | 'b'; +>Union : Union + +type Product = { f1: A, f2: B}; +>Product : Product +>f1 : A +>f2 : B + +type ProductUnion = Product<'a', 0> | Product<'b', 1>; +>ProductUnion : ProductUnion + +// {a: "b"; b: "a"} +type UnionComplement = { +>UnionComplement : UnionComplement + + [K in Union]: Exclude +}; +type UCA = UnionComplement['a']; +>UCA : "b" + +type UCB = UnionComplement['b']; +>UCB : "a" + +// {a: "a"; b: "b"} +type UnionComplementComplement = { +>UnionComplementComplement : UnionComplementComplement + + [K in Union]: Exclude> +}; +type UCCA = UnionComplementComplement['a']; +>UCCA : "a" + +type UCCB = UnionComplementComplement['b']; +>UCCB : "b" + +// {a: Product<'b', 1>; b: Product<'a', 0>} +type ProductComplement = { +>ProductComplement : ProductComplement + + [K in Union]: Exclude +>f1 : K + +}; +type PCA = ProductComplement['a']; +>PCA : Product<"b", 1> + +type PCB = ProductComplement['b']; +>PCB : Product<"a", 0> + +// {a: Product<'a', 0>; b: Product<'b', 1>} +type ProductComplementComplement = { +>ProductComplementComplement : ProductComplementComplement + + [K in Union]: Exclude> +>f1 : K + +}; +type PCCA = ProductComplementComplement['a']; +>PCCA : Product<"a", 0> + +type PCCB = ProductComplementComplement['b']; +>PCCB : Product<"b", 1> + diff --git a/tests/baselines/reference/conditionalTypesExcessProperties.errors.txt b/tests/baselines/reference/conditionalTypesExcessProperties.errors.txt index 54e8c8742a759..24669d3426d93 100644 --- a/tests/baselines/reference/conditionalTypesExcessProperties.errors.txt +++ b/tests/baselines/reference/conditionalTypesExcessProperties.errors.txt @@ -1,7 +1,7 @@ tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(8,5): error TS2322: Type '{ test: string; arg: A; }' is not assignable to type 'Something'. Type '{ test: string; arg: A; }' is not assignable to type 'A extends object ? { arg: A; } : { arg?: undefined; }'. -tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(9,33): error TS2322: Type '{ test: string; arg: A; arr: A; }' is not assignable to type 'Something'. - Object literal may only specify known properties, and 'arr' does not exist in type 'Something'. +tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(9,5): error TS2322: Type '{ test: string; arg: A; arr: A; }' is not assignable to type 'Something'. + Type '{ test: string; arg: A; arr: A; }' is not assignable to type 'A extends object ? { arg: A; } : { arg?: undefined; }'. ==== tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts (2 errors) ==== @@ -17,8 +17,8 @@ tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(9, !!! error TS2322: Type '{ test: string; arg: A; }' is not assignable to type 'Something'. !!! error TS2322: Type '{ test: string; arg: A; }' is not assignable to type 'A extends object ? { arg: A; } : { arg?: undefined; }'. sa = { test: 'bye', arg: a, arr: a } // excess - ~~~~~~ + ~~ !!! error TS2322: Type '{ test: string; arg: A; arr: A; }' is not assignable to type 'Something'. -!!! error TS2322: Object literal may only specify known properties, and 'arr' does not exist in type 'Something'. +!!! error TS2322: Type '{ test: string; arg: A; arr: A; }' is not assignable to type 'A extends object ? { arg: A; } : { arg?: undefined; }'. } \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowDestructuringLoop.js b/tests/baselines/reference/controlFlowDestructuringLoop.js new file mode 100644 index 0000000000000..d7ff51b825978 --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringLoop.js @@ -0,0 +1,43 @@ +//// [controlFlowDestructuringLoop.ts] +// Repro from #28758 + +interface NumVal { val: number; } +interface StrVal { val: string; } +type Val = NumVal | StrVal; + +function isNumVal(x: Val): x is NumVal { + return typeof x.val === 'number'; +} + +function foo(things: Val[]): void { + for (const thing of things) { + if (isNumVal(thing)) { + const { val } = thing; + val.toFixed(2); + } + else { + const { val } = thing; + val.length; + } + } +} + +//// [controlFlowDestructuringLoop.js] +"use strict"; +// Repro from #28758 +function isNumVal(x) { + return typeof x.val === 'number'; +} +function foo(things) { + for (var _i = 0, things_1 = things; _i < things_1.length; _i++) { + var thing = things_1[_i]; + if (isNumVal(thing)) { + var val = thing.val; + val.toFixed(2); + } + else { + var val = thing.val; + val.length; + } + } +} diff --git a/tests/baselines/reference/controlFlowDestructuringLoop.symbols b/tests/baselines/reference/controlFlowDestructuringLoop.symbols new file mode 100644 index 0000000000000..46b7ecd58428b --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringLoop.symbols @@ -0,0 +1,63 @@ +=== tests/cases/compiler/controlFlowDestructuringLoop.ts === +// Repro from #28758 + +interface NumVal { val: number; } +>NumVal : Symbol(NumVal, Decl(controlFlowDestructuringLoop.ts, 0, 0)) +>val : Symbol(NumVal.val, Decl(controlFlowDestructuringLoop.ts, 2, 18)) + +interface StrVal { val: string; } +>StrVal : Symbol(StrVal, Decl(controlFlowDestructuringLoop.ts, 2, 33)) +>val : Symbol(StrVal.val, Decl(controlFlowDestructuringLoop.ts, 3, 18)) + +type Val = NumVal | StrVal; +>Val : Symbol(Val, Decl(controlFlowDestructuringLoop.ts, 3, 33)) +>NumVal : Symbol(NumVal, Decl(controlFlowDestructuringLoop.ts, 0, 0)) +>StrVal : Symbol(StrVal, Decl(controlFlowDestructuringLoop.ts, 2, 33)) + +function isNumVal(x: Val): x is NumVal { +>isNumVal : Symbol(isNumVal, Decl(controlFlowDestructuringLoop.ts, 4, 27)) +>x : Symbol(x, Decl(controlFlowDestructuringLoop.ts, 6, 18)) +>Val : Symbol(Val, Decl(controlFlowDestructuringLoop.ts, 3, 33)) +>x : Symbol(x, Decl(controlFlowDestructuringLoop.ts, 6, 18)) +>NumVal : Symbol(NumVal, Decl(controlFlowDestructuringLoop.ts, 0, 0)) + + return typeof x.val === 'number'; +>x.val : Symbol(val, Decl(controlFlowDestructuringLoop.ts, 2, 18), Decl(controlFlowDestructuringLoop.ts, 3, 18)) +>x : Symbol(x, Decl(controlFlowDestructuringLoop.ts, 6, 18)) +>val : Symbol(val, Decl(controlFlowDestructuringLoop.ts, 2, 18), Decl(controlFlowDestructuringLoop.ts, 3, 18)) +} + +function foo(things: Val[]): void { +>foo : Symbol(foo, Decl(controlFlowDestructuringLoop.ts, 8, 1)) +>things : Symbol(things, Decl(controlFlowDestructuringLoop.ts, 10, 13)) +>Val : Symbol(Val, Decl(controlFlowDestructuringLoop.ts, 3, 33)) + + for (const thing of things) { +>thing : Symbol(thing, Decl(controlFlowDestructuringLoop.ts, 11, 14)) +>things : Symbol(things, Decl(controlFlowDestructuringLoop.ts, 10, 13)) + + if (isNumVal(thing)) { +>isNumVal : Symbol(isNumVal, Decl(controlFlowDestructuringLoop.ts, 4, 27)) +>thing : Symbol(thing, Decl(controlFlowDestructuringLoop.ts, 11, 14)) + + const { val } = thing; +>val : Symbol(val, Decl(controlFlowDestructuringLoop.ts, 13, 19)) +>thing : Symbol(thing, Decl(controlFlowDestructuringLoop.ts, 11, 14)) + + val.toFixed(2); +>val.toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) +>val : Symbol(val, Decl(controlFlowDestructuringLoop.ts, 13, 19)) +>toFixed : Symbol(Number.toFixed, Decl(lib.es5.d.ts, --, --)) + } + else { + const { val } = thing; +>val : Symbol(val, Decl(controlFlowDestructuringLoop.ts, 17, 19)) +>thing : Symbol(thing, Decl(controlFlowDestructuringLoop.ts, 11, 14)) + + val.length; +>val.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>val : Symbol(val, Decl(controlFlowDestructuringLoop.ts, 17, 19)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + } + } +} diff --git a/tests/baselines/reference/controlFlowDestructuringLoop.types b/tests/baselines/reference/controlFlowDestructuringLoop.types new file mode 100644 index 0000000000000..a4735399f9e4a --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringLoop.types @@ -0,0 +1,61 @@ +=== tests/cases/compiler/controlFlowDestructuringLoop.ts === +// Repro from #28758 + +interface NumVal { val: number; } +>val : number + +interface StrVal { val: string; } +>val : string + +type Val = NumVal | StrVal; +>Val : Val + +function isNumVal(x: Val): x is NumVal { +>isNumVal : (x: Val) => x is NumVal +>x : Val + + return typeof x.val === 'number'; +>typeof x.val === 'number' : boolean +>typeof x.val : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x.val : string | number +>x : Val +>val : string | number +>'number' : "number" +} + +function foo(things: Val[]): void { +>foo : (things: Val[]) => void +>things : Val[] + + for (const thing of things) { +>thing : Val +>things : Val[] + + if (isNumVal(thing)) { +>isNumVal(thing) : boolean +>isNumVal : (x: Val) => x is NumVal +>thing : Val + + const { val } = thing; +>val : number +>thing : NumVal + + val.toFixed(2); +>val.toFixed(2) : string +>val.toFixed : (fractionDigits?: number | undefined) => string +>val : number +>toFixed : (fractionDigits?: number | undefined) => string +>2 : 2 + } + else { + const { val } = thing; +>val : string +>thing : StrVal + + val.length; +>val.length : number +>val : string +>length : number + } + } +} diff --git a/tests/baselines/reference/infiniteConstraints.types b/tests/baselines/reference/infiniteConstraints.types index e23e138c820d6..c7d4c07ff4b3d 100644 --- a/tests/baselines/reference/infiniteConstraints.types +++ b/tests/baselines/reference/infiniteConstraints.types @@ -15,7 +15,7 @@ type AProp = T >a : string declare function myBug< ->myBug : (arg: T) => T +>myBug : (arg: T) => T T extends { [K in keyof T]: T[K] extends AProp ? U : never } >(arg: T): T @@ -24,7 +24,7 @@ declare function myBug< const out = myBug({obj1: {a: "test"}}) >out : { obj1: { a: string; }; } >myBug({obj1: {a: "test"}}) : { obj1: { a: string; }; } ->myBug : (arg: T) => T +>myBug : (arg: T) => T >{obj1: {a: "test"}} : { obj1: { a: string; }; } >obj1 : { a: string; } >{a: "test"} : { a: string; } diff --git a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt index fa76abac0dec7..4b034b06206df 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.errors.txt +++ b/tests/baselines/reference/objectLiteralExcessProperties.errors.txt @@ -1,7 +1,7 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(9,18): error TS2322: Type '{ forword: string; }' is not assignable to type 'Book'. Object literal may only specify known properties, but 'forword' does not exist in type 'Book'. Did you mean to write 'foreword'? tests/cases/compiler/objectLiteralExcessProperties.ts(11,27): error TS2322: Type '{ foreward: string; }' is not assignable to type 'string | Book'. - Object literal may only specify known properties, and 'foreward' does not exist in type 'string | Book'. + Object literal may only specify known properties, but 'foreward' does not exist in type 'Book'. Did you mean to write 'foreword'? tests/cases/compiler/objectLiteralExcessProperties.ts(13,53): error TS2322: Type '({ foreword: string; } | { forwards: string; })[]' is not assignable to type 'Book | Book[]'. Type '({ foreword: string; } | { forwards: string; })[]' is not assignable to type 'Book[]'. Type '{ foreword: string; } | { forwards: string; }' is not assignable to type 'Book'. @@ -13,17 +13,27 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(17,26): error TS2322: Type Object literal may only specify known properties, but 'foreward' does not exist in type 'Book & Cover'. Did you mean to write 'foreword'? tests/cases/compiler/objectLiteralExcessProperties.ts(19,57): error TS2322: Type '{ foreword: string; color: string; price: number; }' is not assignable to type 'Book & Cover'. Object literal may only specify known properties, and 'price' does not exist in type 'Book & Cover'. -tests/cases/compiler/objectLiteralExcessProperties.ts(21,43): error TS2322: Type '{ foreword: string; price: number; }' is not assignable to type 'Book & number'. - Object literal may only specify known properties, and 'price' does not exist in type 'Book & number'. +tests/cases/compiler/objectLiteralExcessProperties.ts(21,5): error TS2322: Type '{ foreword: string; price: number; }' is not assignable to type 'Book & number'. + Type '{ foreword: string; price: number; }' is not assignable to type 'number'. tests/cases/compiler/objectLiteralExcessProperties.ts(23,29): error TS2322: Type '{ couleur: string; }' is not assignable to type 'Cover | Cover[]'. Object literal may only specify known properties, and 'couleur' does not exist in type 'Cover | Cover[]'. tests/cases/compiler/objectLiteralExcessProperties.ts(25,27): error TS2322: Type '{ forewarned: string; }' is not assignable to type 'Book | Book[]'. Object literal may only specify known properties, and 'forewarned' does not exist in type 'Book | Book[]'. tests/cases/compiler/objectLiteralExcessProperties.ts(33,27): error TS2322: Type '{ colour: string; }' is not assignable to type 'Cover'. Object literal may only specify known properties, but 'colour' does not exist in type 'Cover'. Did you mean to write 'color'? +tests/cases/compiler/objectLiteralExcessProperties.ts(37,25): error TS2304: Cannot find name 'IFoo'. +tests/cases/compiler/objectLiteralExcessProperties.ts(39,11): error TS2322: Type '{ name: string; }' is not assignable to type 'T'. +tests/cases/compiler/objectLiteralExcessProperties.ts(41,11): error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type 'T & { prop: boolean; }'. + Type '{ name: string; prop: boolean; }' is not assignable to type 'T'. +tests/cases/compiler/objectLiteralExcessProperties.ts(43,43): error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type 'T | { prop: boolean; }'. + Object literal may only specify known properties, and 'name' does not exist in type '{ prop: boolean; }'. +tests/cases/compiler/objectLiteralExcessProperties.ts(45,76): error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type '{ name: string; } | (T & { prop: boolean; })'. + Object literal may only specify known properties, and 'prop' does not exist in type '{ name: string; }'. +tests/cases/compiler/objectLiteralExcessProperties.ts(49,44): error TS2322: Type '{ z: string; }' is not assignable to type 'object & { x: string; }'. + Object literal may only specify known properties, and 'z' does not exist in type 'object & { x: string; }'. -==== tests/cases/compiler/objectLiteralExcessProperties.ts (10 errors) ==== +==== tests/cases/compiler/objectLiteralExcessProperties.ts (16 errors) ==== interface Book { foreword: string; } @@ -40,7 +50,7 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(33,27): error TS2322: Type var b2: Book | string = { foreward: "nope" }; ~~~~~~~~~~~~~~~~ !!! error TS2322: Type '{ foreward: string; }' is not assignable to type 'string | Book'. -!!! error TS2322: Object literal may only specify known properties, and 'foreward' does not exist in type 'string | Book'. +!!! error TS2322: Object literal may only specify known properties, but 'foreward' does not exist in type 'Book'. Did you mean to write 'foreword'? var b3: Book | (Book[]) = [{ foreword: "hello" }, { forwards: "back" }]; ~~~~~~~~~~~~~~~~ @@ -66,9 +76,9 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(33,27): error TS2322: Type !!! error TS2322: Object literal may only specify known properties, and 'price' does not exist in type 'Book & Cover'. var b7: Book & number = { foreword: "hi", price: 10.99 }; - ~~~~~~~~~~~~ + ~~ !!! error TS2322: Type '{ foreword: string; price: number; }' is not assignable to type 'Book & number'. -!!! error TS2322: Object literal may only specify known properties, and 'price' does not exist in type 'Book & number'. +!!! error TS2322: Type '{ foreword: string; price: number; }' is not assignable to type 'number'. var b8: Cover | Cover[] = { couleur : "non" }; ~~~~~~~~~~~~~~~ @@ -91,4 +101,37 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(33,27): error TS2322: Type !!! error TS2322: Type '{ colour: string; }' is not assignable to type 'Cover'. !!! error TS2322: Object literal may only specify known properties, but 'colour' does not exist in type 'Cover'. Did you mean to write 'color'? !!! related TS6501 tests/cases/compiler/objectLiteralExcessProperties.ts:28:5: The expected type comes from this index signature. + + // Repros inspired by #28752 + + function test() { + ~~~~ +!!! error TS2304: Cannot find name 'IFoo'. + // No excess property checks on generic types + const obj1: T = { name: "test" }; + ~~~~ +!!! error TS2322: Type '{ name: string; }' is not assignable to type 'T'. + // No excess property checks on intersections involving generics + const obj2: T & { prop: boolean } = { name: "test", prop: true }; + ~~~~ +!!! error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type 'T & { prop: boolean; }'. +!!! error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type 'T'. + // Excess property checks only on non-generic parts of unions + const obj3: T | { prop: boolean } = { name: "test", prop: true }; + ~~~~~~~~~~~~ +!!! error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type 'T | { prop: boolean; }'. +!!! error TS2322: Object literal may only specify known properties, and 'name' does not exist in type '{ prop: boolean; }'. + // Excess property checks only on non-generic parts of unions + const obj4: T & { prop: boolean } | { name: string } = { name: "test", prop: true }; + ~~~~~~~~~~ +!!! error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type '{ name: string; } | (T & { prop: boolean; })'. +!!! error TS2322: Object literal may only specify known properties, and 'prop' does not exist in type '{ name: string; }'. + // No excess property checks when union includes 'object' type + const obj5: object | { x: string } = { z: 'abc' } + // The 'object' type has no effect on intersections + const obj6: object & { x: string } = { z: 'abc' } + ~~~~~~~~ +!!! error TS2322: Type '{ z: string; }' is not assignable to type 'object & { x: string; }'. +!!! error TS2322: Object literal may only specify known properties, and 'z' does not exist in type 'object & { x: string; }'. + } \ No newline at end of file diff --git a/tests/baselines/reference/objectLiteralExcessProperties.js b/tests/baselines/reference/objectLiteralExcessProperties.js index 38186b19bf707..5df0aebc12daf 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.js +++ b/tests/baselines/reference/objectLiteralExcessProperties.js @@ -32,6 +32,23 @@ interface Indexed { var b10: Indexed = { 0: { }, '1': { } }; // ok var b11: Indexed = { 0: { colour: "blue" } }; // nested object literal still errors + +// Repros inspired by #28752 + +function test() { + // No excess property checks on generic types + const obj1: T = { name: "test" }; + // No excess property checks on intersections involving generics + const obj2: T & { prop: boolean } = { name: "test", prop: true }; + // Excess property checks only on non-generic parts of unions + const obj3: T | { prop: boolean } = { name: "test", prop: true }; + // Excess property checks only on non-generic parts of unions + const obj4: T & { prop: boolean } | { name: string } = { name: "test", prop: true }; + // No excess property checks when union includes 'object' type + const obj5: object | { x: string } = { z: 'abc' } + // The 'object' type has no effect on intersections + const obj6: object & { x: string } = { z: 'abc' } +} //// [objectLiteralExcessProperties.js] @@ -46,3 +63,18 @@ var b8 = { couleur: "non" }; var b9 = { forewarned: "still no" }; var b10 = { 0: {}, '1': {} }; // ok var b11 = { 0: { colour: "blue" } }; // nested object literal still errors +// Repros inspired by #28752 +function test() { + // No excess property checks on generic types + var obj1 = { name: "test" }; + // No excess property checks on intersections involving generics + var obj2 = { name: "test", prop: true }; + // Excess property checks only on non-generic parts of unions + var obj3 = { name: "test", prop: true }; + // Excess property checks only on non-generic parts of unions + var obj4 = { name: "test", prop: true }; + // No excess property checks when union includes 'object' type + var obj5 = { z: 'abc' }; + // The 'object' type has no effect on intersections + var obj6 = { z: 'abc' }; +} diff --git a/tests/baselines/reference/objectLiteralExcessProperties.symbols b/tests/baselines/reference/objectLiteralExcessProperties.symbols index d145f43cd5f6f..3624766ba0dd2 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.symbols +++ b/tests/baselines/reference/objectLiteralExcessProperties.symbols @@ -90,3 +90,53 @@ var b11: Indexed = { 0: { colour: "blue" } }; // nested object literal still err >0 : Symbol(0, Decl(objectLiteralExcessProperties.ts, 32, 20)) >colour : Symbol(colour, Decl(objectLiteralExcessProperties.ts, 32, 25)) +// Repros inspired by #28752 + +function test() { +>test : Symbol(test, Decl(objectLiteralExcessProperties.ts, 32, 45)) +>T : Symbol(T, Decl(objectLiteralExcessProperties.ts, 36, 14)) + + // No excess property checks on generic types + const obj1: T = { name: "test" }; +>obj1 : Symbol(obj1, Decl(objectLiteralExcessProperties.ts, 38, 9)) +>T : Symbol(T, Decl(objectLiteralExcessProperties.ts, 36, 14)) +>name : Symbol(name, Decl(objectLiteralExcessProperties.ts, 38, 21)) + + // No excess property checks on intersections involving generics + const obj2: T & { prop: boolean } = { name: "test", prop: true }; +>obj2 : Symbol(obj2, Decl(objectLiteralExcessProperties.ts, 40, 9)) +>T : Symbol(T, Decl(objectLiteralExcessProperties.ts, 36, 14)) +>prop : Symbol(prop, Decl(objectLiteralExcessProperties.ts, 40, 21)) +>name : Symbol(name, Decl(objectLiteralExcessProperties.ts, 40, 41)) +>prop : Symbol(prop, Decl(objectLiteralExcessProperties.ts, 40, 55)) + + // Excess property checks only on non-generic parts of unions + const obj3: T | { prop: boolean } = { name: "test", prop: true }; +>obj3 : Symbol(obj3, Decl(objectLiteralExcessProperties.ts, 42, 9)) +>T : Symbol(T, Decl(objectLiteralExcessProperties.ts, 36, 14)) +>prop : Symbol(prop, Decl(objectLiteralExcessProperties.ts, 42, 21)) +>name : Symbol(name, Decl(objectLiteralExcessProperties.ts, 42, 41)) +>prop : Symbol(prop, Decl(objectLiteralExcessProperties.ts, 42, 55)) + + // Excess property checks only on non-generic parts of unions + const obj4: T & { prop: boolean } | { name: string } = { name: "test", prop: true }; +>obj4 : Symbol(obj4, Decl(objectLiteralExcessProperties.ts, 44, 9)) +>T : Symbol(T, Decl(objectLiteralExcessProperties.ts, 36, 14)) +>prop : Symbol(prop, Decl(objectLiteralExcessProperties.ts, 44, 21)) +>name : Symbol(name, Decl(objectLiteralExcessProperties.ts, 44, 41)) +>name : Symbol(name, Decl(objectLiteralExcessProperties.ts, 44, 60)) +>prop : Symbol(prop, Decl(objectLiteralExcessProperties.ts, 44, 74)) + + // No excess property checks when union includes 'object' type + const obj5: object | { x: string } = { z: 'abc' } +>obj5 : Symbol(obj5, Decl(objectLiteralExcessProperties.ts, 46, 9)) +>x : Symbol(x, Decl(objectLiteralExcessProperties.ts, 46, 26)) +>z : Symbol(z, Decl(objectLiteralExcessProperties.ts, 46, 42)) + + // The 'object' type has no effect on intersections + const obj6: object & { x: string } = { z: 'abc' } +>obj6 : Symbol(obj6, Decl(objectLiteralExcessProperties.ts, 48, 9)) +>x : Symbol(x, Decl(objectLiteralExcessProperties.ts, 48, 26)) +>z : Symbol(z, Decl(objectLiteralExcessProperties.ts, 48, 42)) +} + diff --git a/tests/baselines/reference/objectLiteralExcessProperties.types b/tests/baselines/reference/objectLiteralExcessProperties.types index 0b1c10c3bec2c..8765432ab7040 100644 --- a/tests/baselines/reference/objectLiteralExcessProperties.types +++ b/tests/baselines/reference/objectLiteralExcessProperties.types @@ -98,3 +98,63 @@ var b11: Indexed = { 0: { colour: "blue" } }; // nested object literal still err >colour : string >"blue" : "blue" +// Repros inspired by #28752 + +function test() { +>test : () => void + + // No excess property checks on generic types + const obj1: T = { name: "test" }; +>obj1 : T +>{ name: "test" } : { name: string; } +>name : string +>"test" : "test" + + // No excess property checks on intersections involving generics + const obj2: T & { prop: boolean } = { name: "test", prop: true }; +>obj2 : T & { prop: boolean; } +>prop : boolean +>{ name: "test", prop: true } : { name: string; prop: boolean; } +>name : string +>"test" : "test" +>prop : boolean +>true : true + + // Excess property checks only on non-generic parts of unions + const obj3: T | { prop: boolean } = { name: "test", prop: true }; +>obj3 : T | { prop: boolean; } +>prop : boolean +>{ name: "test", prop: true } : { name: string; prop: boolean; } +>name : string +>"test" : "test" +>prop : boolean +>true : true + + // Excess property checks only on non-generic parts of unions + const obj4: T & { prop: boolean } | { name: string } = { name: "test", prop: true }; +>obj4 : { name: string; } | (T & { prop: boolean; }) +>prop : boolean +>name : string +>{ name: "test", prop: true } : { name: string; prop: boolean; } +>name : string +>"test" : "test" +>prop : boolean +>true : true + + // No excess property checks when union includes 'object' type + const obj5: object | { x: string } = { z: 'abc' } +>obj5 : object | { x: string; } +>x : string +>{ z: 'abc' } : { z: string; } +>z : string +>'abc' : "abc" + + // The 'object' type has no effect on intersections + const obj6: object & { x: string } = { z: 'abc' } +>obj6 : object & { x: string; } +>x : string +>{ z: 'abc' } : { z: string; } +>z : string +>'abc' : "abc" +} + diff --git a/tests/baselines/reference/tsxIntrinsicAttributeErrors.errors.txt b/tests/baselines/reference/tsxIntrinsicAttributeErrors.errors.txt index ae3f26d641ecc..c26eb2f7d3dbb 100644 --- a/tests/baselines/reference/tsxIntrinsicAttributeErrors.errors.txt +++ b/tests/baselines/reference/tsxIntrinsicAttributeErrors.errors.txt @@ -1,5 +1,4 @@ -tests/cases/conformance/jsx/tsxIntrinsicAttributeErrors.tsx(29,2): error TS2322: Type '{ x: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<{ x: number; render(): void; }> & string'. - Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<{ x: number; render(): void; }> & string'. +tests/cases/conformance/jsx/tsxIntrinsicAttributeErrors.tsx(29,2): error TS2741: Property 'key' is missing in type '{ x: number; }' but required in type 'IntrinsicAttributes'. ==== tests/cases/conformance/jsx/tsxIntrinsicAttributeErrors.tsx (1 errors) ==== @@ -33,6 +32,6 @@ tests/cases/conformance/jsx/tsxIntrinsicAttributeErrors.tsx(29,2): error TS2322: var E: I; ~ -!!! error TS2322: Type '{ x: number; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<{ x: number; render(): void; }> & string'. -!!! error TS2322: Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<{ x: number; render(): void; }> & string'. +!!! error TS2741: Property 'key' is missing in type '{ x: number; }' but required in type 'IntrinsicAttributes'. +!!! related TS2728 tests/cases/conformance/jsx/tsxIntrinsicAttributeErrors.tsx:7:9: 'key' is declared here. \ No newline at end of file diff --git a/tests/baselines/reference/unknownType1.types b/tests/baselines/reference/unknownType1.types index aaaf73bb7098a..bf177d4fde863 100644 --- a/tests/baselines/reference/unknownType1.types +++ b/tests/baselines/reference/unknownType1.types @@ -76,7 +76,7 @@ type T31 = T extends unknown ? true : false; // Deferred (so it distributes) >false : false type T32 = never extends T ? true : false; // true ->T32 : true +>T32 : T32 >true : true >false : false diff --git a/tests/cases/compiler/controlFlowDestructuringLoop.ts b/tests/cases/compiler/controlFlowDestructuringLoop.ts new file mode 100644 index 0000000000000..bf3a68cb2d8d2 --- /dev/null +++ b/tests/cases/compiler/controlFlowDestructuringLoop.ts @@ -0,0 +1,24 @@ +// @strict: true + +// Repro from #28758 + +interface NumVal { val: number; } +interface StrVal { val: string; } +type Val = NumVal | StrVal; + +function isNumVal(x: Val): x is NumVal { + return typeof x.val === 'number'; +} + +function foo(things: Val[]): void { + for (const thing of things) { + if (isNumVal(thing)) { + const { val } = thing; + val.toFixed(2); + } + else { + const { val } = thing; + val.length; + } + } +} \ No newline at end of file diff --git a/tests/cases/compiler/objectLiteralExcessProperties.ts b/tests/cases/compiler/objectLiteralExcessProperties.ts index ca681b7b28ee2..660529ec3ffa5 100644 --- a/tests/cases/compiler/objectLiteralExcessProperties.ts +++ b/tests/cases/compiler/objectLiteralExcessProperties.ts @@ -31,3 +31,20 @@ interface Indexed { var b10: Indexed = { 0: { }, '1': { } }; // ok var b11: Indexed = { 0: { colour: "blue" } }; // nested object literal still errors + +// Repros inspired by #28752 + +function test() { + // No excess property checks on generic types + const obj1: T = { name: "test" }; + // No excess property checks on intersections involving generics + const obj2: T & { prop: boolean } = { name: "test", prop: true }; + // Excess property checks only on non-generic parts of unions + const obj3: T | { prop: boolean } = { name: "test", prop: true }; + // Excess property checks only on non-generic parts of unions + const obj4: T & { prop: boolean } | { name: string } = { name: "test", prop: true }; + // No excess property checks when union includes 'object' type + const obj5: object | { x: string } = { z: 'abc' } + // The 'object' type has no effect on intersections + const obj6: object & { x: string } = { z: 'abc' } +} diff --git a/tests/cases/conformance/types/conditional/conditionalTypes2.ts b/tests/cases/conformance/types/conditional/conditionalTypes2.ts index be75894546944..4b65b5ddeb202 100644 --- a/tests/cases/conformance/types/conditional/conditionalTypes2.ts +++ b/tests/cases/conformance/types/conditional/conditionalTypes2.ts @@ -147,3 +147,46 @@ type B2 = type C2 = T extends object ? { [Q in keyof T]: C2; } : T; + +// Repro from #28654 + +type MaybeTrue = true extends T["b"] ? "yes" : "no"; + +type T0 = MaybeTrue<{ b: never }> // "no" +type T1 = MaybeTrue<{ b: false }>; // "no" +type T2 = MaybeTrue<{ b: true }>; // "yes" +type T3 = MaybeTrue<{ b: boolean }>; // "yes" + +// Repro from #28824 + +type Union = 'a' | 'b'; +type Product = { f1: A, f2: B}; +type ProductUnion = Product<'a', 0> | Product<'b', 1>; + +// {a: "b"; b: "a"} +type UnionComplement = { + [K in Union]: Exclude +}; +type UCA = UnionComplement['a']; +type UCB = UnionComplement['b']; + +// {a: "a"; b: "b"} +type UnionComplementComplement = { + [K in Union]: Exclude> +}; +type UCCA = UnionComplementComplement['a']; +type UCCB = UnionComplementComplement['b']; + +// {a: Product<'b', 1>; b: Product<'a', 0>} +type ProductComplement = { + [K in Union]: Exclude +}; +type PCA = ProductComplement['a']; +type PCB = ProductComplement['b']; + +// {a: Product<'a', 0>; b: Product<'b', 1>} +type ProductComplementComplement = { + [K in Union]: Exclude> +}; +type PCCA = ProductComplementComplement['a']; +type PCCB = ProductComplementComplement['b'];