mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Consistently propagate intersectionState in relations (#52392)
This commit is contained in:
1 parent
9ee33156f5
commit
30993855fd
7 files changed
+226
-51
No files matched your search
+49
-39
@@ -1261,8 +1261,8 @@ export const enum SignatureCheckMode {
|
||||
|
||||
const enum IntersectionState {
|
||||
None = 0,
|
||||
Source = 1 << 0,
|
||||
Target = 1 << 1,
|
||||
Source = 1 << 0, // Source type is a constituent of an outer intersection
|
||||
Target = 1 << 1, // Target type is a constituent of an outer intersection
|
||||
}
|
||||
|
||||
const enum RecursionFlags {
|
||||
@@ -19990,7 +19990,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
let overrideNextErrorInfo = 0; // How many `reportRelationError` calls should be skipped in the elaboration pyramid
|
||||
let lastSkippedInfo: [Type, Type] | undefined;
|
||||
let incompatibleStack: [DiagnosticMessage, (string | number)?, (string | number)?, (string | number)?, (string | number)?][] | undefined;
|
||||
let inPropertyCheck = false;
|
||||
|
||||
Debug.assert(relation !== identityRelation || !errorNode, "no error reporting in identity checking");
|
||||
|
||||
@@ -20955,30 +20954,33 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
|
||||
}
|
||||
}
|
||||
// For certain combinations involving intersections and optional, excess, or mismatched properties we need
|
||||
// an extra property check where the intersection is viewed as a single object. The following are motivating
|
||||
// examples that all should be errors, but aren't without this extra property check:
|
||||
// When the target is an intersection we need an extra property check in order to detect nested excess
|
||||
// properties and nested weak types. The following are motivating examples that all should be errors, but
|
||||
// aren't without this extra property check:
|
||||
//
|
||||
// let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
|
||||
//
|
||||
// declare let wrong: { a: { y: string } };
|
||||
// let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
|
||||
//
|
||||
if (result && !(intersectionState & IntersectionState.Target) && target.flags & TypeFlags.Intersection &&
|
||||
!isGenericObjectType(target) && source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
|
||||
result &= propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, /*optionalsOnly*/ false, IntersectionState.None);
|
||||
if (result && isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral) {
|
||||
result &= indexSignaturesRelatedTo(source, target, /*sourceIsPrimitive*/ false, reportErrors, IntersectionState.None);
|
||||
}
|
||||
}
|
||||
// When the source is an intersection we need an extra check of any optional properties in the target to
|
||||
// detect possible mismatched property types. For example:
|
||||
//
|
||||
// function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
|
||||
// x = y; // Mismatched property in source intersection
|
||||
// }
|
||||
//
|
||||
// We suppress recursive intersection property checks because they can generate lots of work when relating
|
||||
// recursive intersections that are structurally similar but not exactly identical. See #37854.
|
||||
if (result && !inPropertyCheck && (
|
||||
target.flags & TypeFlags.Intersection && !isGenericObjectType(target) && source.flags & (TypeFlags.Object | TypeFlags.Intersection) ||
|
||||
isNonGenericObjectType(target) && !isArrayOrTupleType(target) && source.flags & TypeFlags.Intersection && getApparentType(source).flags & TypeFlags.StructuredType && !some((source as IntersectionType).types, t => !!(getObjectFlags(t) & ObjectFlags.NonInferrableType)))) {
|
||||
inPropertyCheck = true;
|
||||
result &= propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
|
||||
if (result && isObjectLiteralType(source) && getObjectFlags(source) & ObjectFlags.FreshLiteral) {
|
||||
result &= indexSignaturesRelatedTo(source, target, /*sourceIsPrimitive*/ false, reportErrors, IntersectionState.None);
|
||||
}
|
||||
inPropertyCheck = false;
|
||||
else if (result && isNonGenericObjectType(target) && !isArrayOrTupleType(target) &&
|
||||
source.flags & TypeFlags.Intersection && getApparentType(source).flags & TypeFlags.StructuredType &&
|
||||
!some((source as IntersectionType).types, t => !!(getObjectFlags(t) & ObjectFlags.NonInferrableType))) {
|
||||
result &= propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, /*optionalsOnly*/ true, intersectionState);
|
||||
}
|
||||
}
|
||||
if (result) {
|
||||
@@ -21486,11 +21488,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (sourceFlags & (TypeFlags.Object | TypeFlags.Intersection) && targetFlags & TypeFlags.Object) {
|
||||
// Report structural errors only if we haven't reported any errors yet
|
||||
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo.errorInfo && !sourceIsPrimitive;
|
||||
result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, intersectionState);
|
||||
result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, /*optionalsOnly*/ false, intersectionState);
|
||||
if (result) {
|
||||
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors);
|
||||
result &= signaturesRelatedTo(source, target, SignatureKind.Call, reportStructuralErrors, intersectionState);
|
||||
if (result) {
|
||||
result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportStructuralErrors);
|
||||
result &= signaturesRelatedTo(source, target, SignatureKind.Construct, reportStructuralErrors, intersectionState);
|
||||
if (result) {
|
||||
result &= indexSignaturesRelatedTo(source, target, sourceIsPrimitive, reportStructuralErrors, intersectionState);
|
||||
}
|
||||
@@ -21661,11 +21663,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// Compare the remaining non-discriminant properties of each match.
|
||||
let result = Ternary.True;
|
||||
for (const type of matchingTypes) {
|
||||
result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, IntersectionState.None);
|
||||
result &= propertiesRelatedTo(source, type, /*reportErrors*/ false, excludedProperties, /*optionalsOnly*/ false, IntersectionState.None);
|
||||
if (result) {
|
||||
result &= signaturesRelatedTo(source, type, SignatureKind.Call, /*reportStructuralErrors*/ false);
|
||||
result &= signaturesRelatedTo(source, type, SignatureKind.Call, /*reportStructuralErrors*/ false, IntersectionState.None);
|
||||
if (result) {
|
||||
result &= signaturesRelatedTo(source, type, SignatureKind.Construct, /*reportStructuralErrors*/ false);
|
||||
result &= signaturesRelatedTo(source, type, SignatureKind.Construct, /*reportStructuralErrors*/ false, IntersectionState.None);
|
||||
if (result && !(isTupleType(source) && isTupleType(type))) {
|
||||
// Comparing numeric index types when both `source` and `type` are tuples is unnecessary as the
|
||||
// element types should be sufficiently covered by `propertiesRelatedTo`. It also causes problems
|
||||
@@ -21828,7 +21830,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// No array like or unmatched property error - just issue top level error (errorInfo = undefined)
|
||||
}
|
||||
|
||||
function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: Set<__String> | undefined, intersectionState: IntersectionState): Ternary {
|
||||
function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: Set<__String> | undefined, optionalsOnly: boolean, intersectionState: IntersectionState): Ternary {
|
||||
if (relation === identityRelation) {
|
||||
return propertiesIdenticalTo(source, target, excludedProperties);
|
||||
}
|
||||
@@ -21963,7 +21965,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const numericNamesOnly = isTupleType(source) && isTupleType(target);
|
||||
for (const targetProp of excludeProperties(properties, excludedProperties)) {
|
||||
const name = targetProp.escapedName;
|
||||
if (!(targetProp.flags & SymbolFlags.Prototype) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) {
|
||||
if (!(targetProp.flags & SymbolFlags.Prototype) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length") && (!optionalsOnly || targetProp.flags & SymbolFlags.Optional)) {
|
||||
const sourceProp = getPropertyOfType(source, name);
|
||||
if (sourceProp && sourceProp !== targetProp) {
|
||||
const related = propertyRelatedTo(source, target, sourceProp, targetProp, getNonMissingTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation);
|
||||
@@ -22001,7 +22003,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return result;
|
||||
}
|
||||
|
||||
function signaturesRelatedTo(source: Type, target: Type, kind: SignatureKind, reportErrors: boolean): Ternary {
|
||||
function signaturesRelatedTo(source: Type, target: Type, kind: SignatureKind, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
|
||||
if (relation === identityRelation) {
|
||||
return signaturesIdenticalTo(source, target, kind);
|
||||
}
|
||||
@@ -22046,7 +22048,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// of the much more expensive N * M comparison matrix we explore below. We erase type parameters
|
||||
// as they are known to always be the same.
|
||||
for (let i = 0; i < targetSignatures.length; i++) {
|
||||
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors, incompatibleReporter(sourceSignatures[i], targetSignatures[i]));
|
||||
const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors, intersectionState, incompatibleReporter(sourceSignatures[i], targetSignatures[i]));
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
@@ -22062,7 +22064,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks;
|
||||
const sourceSignature = first(sourceSignatures);
|
||||
const targetSignature = first(targetSignatures);
|
||||
result = signatureRelatedTo(sourceSignature, targetSignature, eraseGenerics, reportErrors, incompatibleReporter(sourceSignature, targetSignature));
|
||||
result = signatureRelatedTo(sourceSignature, targetSignature, eraseGenerics, reportErrors, intersectionState, incompatibleReporter(sourceSignature, targetSignature));
|
||||
if (!result && reportErrors && kind === SignatureKind.Construct && (sourceObjectFlags & targetObjectFlags) &&
|
||||
(targetSignature.declaration?.kind === SyntaxKind.Constructor || sourceSignature.declaration?.kind === SyntaxKind.Constructor)) {
|
||||
const constructSignatureToString = (signature: Signature) =>
|
||||
@@ -22078,7 +22080,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// Only elaborate errors from the first failure
|
||||
let shouldElaborateErrors = reportErrors;
|
||||
for (const s of sourceSignatures) {
|
||||
const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors, incompatibleReporter(s, t));
|
||||
const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors, intersectionState, incompatibleReporter(s, t));
|
||||
if (related) {
|
||||
result &= related;
|
||||
resetErrorInfo(saveErrorInfo);
|
||||
@@ -22128,9 +22130,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
/**
|
||||
* See signatureAssignableTo, compareSignaturesIdentical
|
||||
*/
|
||||
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
|
||||
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, intersectionState: IntersectionState, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
|
||||
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
|
||||
relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedToWorker, reportUnreliableMapper);
|
||||
function isRelatedToWorker(source: Type, target: Type, reportErrors?: boolean) {
|
||||
return isRelatedTo(source, target, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
|
||||
}
|
||||
}
|
||||
|
||||
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
|
||||
@@ -22176,7 +22181,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
for (const info of getIndexInfosOfType(source)) {
|
||||
if (isApplicableIndexType(info.keyType, keyType)) {
|
||||
const related = indexInfoRelatedTo(info, targetInfo, reportErrors);
|
||||
const related = indexInfoRelatedTo(info, targetInfo, reportErrors, intersectionState);
|
||||
if (!related) {
|
||||
return Ternary.False;
|
||||
}
|
||||
@@ -22186,8 +22191,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return result;
|
||||
}
|
||||
|
||||
function indexInfoRelatedTo(sourceInfo: IndexInfo, targetInfo: IndexInfo, reportErrors: boolean) {
|
||||
const related = isRelatedTo(sourceInfo.type, targetInfo.type, RecursionFlags.Both, reportErrors);
|
||||
function indexInfoRelatedTo(sourceInfo: IndexInfo, targetInfo: IndexInfo, reportErrors: boolean, intersectionState: IntersectionState) {
|
||||
const related = isRelatedTo(sourceInfo.type, targetInfo.type, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
|
||||
if (!related && reportErrors) {
|
||||
if (sourceInfo.keyType === targetInfo.keyType) {
|
||||
reportError(Diagnostics._0_index_signatures_are_incompatible, typeToString(sourceInfo.keyType));
|
||||
@@ -22221,7 +22226,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
function typeRelatedToIndexInfo(source: Type, targetInfo: IndexInfo, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
|
||||
const sourceInfo = getApplicableIndexInfo(source, targetInfo.keyType);
|
||||
if (sourceInfo) {
|
||||
return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors);
|
||||
return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors, intersectionState);
|
||||
}
|
||||
if (!(intersectionState & IntersectionState.Source) && isObjectTypeWithInferableIndex(source)) {
|
||||
// Intersection constituents are never considered to have an inferred index signature
|
||||
@@ -22597,10 +22602,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
// Return true if the given type is deeply nested. We consider this to be the case when structural type comparisons
|
||||
// for maxDepth or more occurrences or instantiations of the type have been recorded on the given stack. It is possible,
|
||||
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely
|
||||
// expanding. Effectively, we will generate a false positive when two types are structurally equal to at least maxDepth
|
||||
// levels, but unequal at some level beyond that.
|
||||
// for maxDepth or more occurrences or instantiations of the same type have been recorded on the given stack. The
|
||||
// "sameness" of instantiations is determined by the getRecursionIdentity function. An intersection is considered
|
||||
// deeply nested if any constituent of the intersection is deeply nested. It is possible, though highly unlikely, for
|
||||
// the deeply nested check to be true in a situation where a chain of instantiations is not infinitely expanding.
|
||||
// Effectively, we will generate a false positive when two types are structurally equal to at least maxDepth levels,
|
||||
// but unequal at some level beyond that.
|
||||
// In addition, this will also detect when an indexed access has been chained off of maxDepth more times (which is
|
||||
// essentially the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding
|
||||
// false positives for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`).
|
||||
@@ -22610,12 +22617,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// to terminate the expansion, and we do so here.
|
||||
function isDeeplyNestedType(type: Type, stack: Type[], depth: number, maxDepth = 3): boolean {
|
||||
if (depth >= maxDepth) {
|
||||
if (type.flags & TypeFlags.Intersection) {
|
||||
return some((type as IntersectionType).types, t => isDeeplyNestedType(t, stack, depth, maxDepth));
|
||||
}
|
||||
const identity = getRecursionIdentity(type);
|
||||
let count = 0;
|
||||
let lastTypeId = 0;
|
||||
for (let i = 0; i < depth; i++) {
|
||||
const t = stack[i];
|
||||
if (getRecursionIdentity(t) === identity) {
|
||||
if (t.flags & TypeFlags.Intersection ? some((t as IntersectionType).types, u => getRecursionIdentity(u) === identity) : getRecursionIdentity(t) === identity) {
|
||||
// We only count occurrences with a higher type id than the previous occurrence, since higher
|
||||
// type ids are an indicator of newer instantiations caused by recursion.
|
||||
if (t.id >= lastTypeId) {
|
||||
|
||||
@@ -67,4 +67,25 @@ tests/cases/compiler/nestedExcessPropertyChecking.ts(40,9): error TS2559: Type '
|
||||
!!! related TS6500 tests/cases/compiler/nestedExcessPropertyChecking.ts:35:24: The expected type comes from property 'overrides' which is declared here on type 'VariablesA & VariablesB'
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified repro from #52252
|
||||
|
||||
type T1 = {
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number, dark: number } },
|
||||
};
|
||||
|
||||
type T2 = {
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number } },
|
||||
};
|
||||
|
||||
type Query = T1 & T2;
|
||||
|
||||
const response: Query = {
|
||||
primary: {
|
||||
colors: {
|
||||
light: 1,
|
||||
dark: 3,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -41,6 +41,27 @@ const foo2: Unrelated & { variables: VariablesA & VariablesB } = {
|
||||
overrides: false // Error
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified repro from #52252
|
||||
|
||||
type T1 = {
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number, dark: number } },
|
||||
};
|
||||
|
||||
type T2 = {
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number } },
|
||||
};
|
||||
|
||||
type Query = T1 & T2;
|
||||
|
||||
const response: Query = {
|
||||
primary: {
|
||||
colors: {
|
||||
light: 1,
|
||||
dark: 3,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
//// [nestedExcessPropertyChecking.js]
|
||||
@@ -59,3 +80,11 @@ var foo2 = {
|
||||
overrides: false // Error
|
||||
}
|
||||
};
|
||||
var response = {
|
||||
primary: {
|
||||
colors: {
|
||||
light: 1,
|
||||
dark: 3,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -113,3 +113,53 @@ const foo2: Unrelated & { variables: VariablesA & VariablesB } = {
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified repro from #52252
|
||||
|
||||
type T1 = {
|
||||
>T1 : Symbol(T1, Decl(nestedExcessPropertyChecking.ts, 41, 2))
|
||||
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number, dark: number } },
|
||||
>primary : Symbol(primary, Decl(nestedExcessPropertyChecking.ts, 45, 11))
|
||||
>__typename : Symbol(__typename, Decl(nestedExcessPropertyChecking.ts, 46, 14))
|
||||
>colors : Symbol(colors, Decl(nestedExcessPropertyChecking.ts, 46, 43))
|
||||
>light : Symbol(light, Decl(nestedExcessPropertyChecking.ts, 46, 53))
|
||||
>dark : Symbol(dark, Decl(nestedExcessPropertyChecking.ts, 46, 68))
|
||||
|
||||
};
|
||||
|
||||
type T2 = {
|
||||
>T2 : Symbol(T2, Decl(nestedExcessPropertyChecking.ts, 47, 2))
|
||||
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number } },
|
||||
>primary : Symbol(primary, Decl(nestedExcessPropertyChecking.ts, 49, 11))
|
||||
>__typename : Symbol(__typename, Decl(nestedExcessPropertyChecking.ts, 50, 14))
|
||||
>colors : Symbol(colors, Decl(nestedExcessPropertyChecking.ts, 50, 43))
|
||||
>light : Symbol(light, Decl(nestedExcessPropertyChecking.ts, 50, 53))
|
||||
|
||||
};
|
||||
|
||||
type Query = T1 & T2;
|
||||
>Query : Symbol(Query, Decl(nestedExcessPropertyChecking.ts, 51, 2))
|
||||
>T1 : Symbol(T1, Decl(nestedExcessPropertyChecking.ts, 41, 2))
|
||||
>T2 : Symbol(T2, Decl(nestedExcessPropertyChecking.ts, 47, 2))
|
||||
|
||||
const response: Query = {
|
||||
>response : Symbol(response, Decl(nestedExcessPropertyChecking.ts, 55, 5))
|
||||
>Query : Symbol(Query, Decl(nestedExcessPropertyChecking.ts, 51, 2))
|
||||
|
||||
primary: {
|
||||
>primary : Symbol(primary, Decl(nestedExcessPropertyChecking.ts, 55, 25))
|
||||
|
||||
colors: {
|
||||
>colors : Symbol(colors, Decl(nestedExcessPropertyChecking.ts, 56, 14))
|
||||
|
||||
light: 1,
|
||||
>light : Symbol(light, Decl(nestedExcessPropertyChecking.ts, 57, 17))
|
||||
|
||||
dark: 3,
|
||||
>dark : Symbol(dark, Decl(nestedExcessPropertyChecking.ts, 58, 21))
|
||||
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -106,3 +106,55 @@ const foo2: Unrelated & { variables: VariablesA & VariablesB } = {
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified repro from #52252
|
||||
|
||||
type T1 = {
|
||||
>T1 : { primary: { __typename?: 'Feature';} & { colors: { light: number; dark: number; };}; }
|
||||
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number, dark: number } },
|
||||
>primary : { __typename?: "Feature" | undefined; } & { colors: { light: number; dark: number;}; }
|
||||
>__typename : "Feature" | undefined
|
||||
>colors : { light: number; dark: number; }
|
||||
>light : number
|
||||
>dark : number
|
||||
|
||||
};
|
||||
|
||||
type T2 = {
|
||||
>T2 : { primary: { __typename?: 'Feature';} & { colors: { light: number; };}; }
|
||||
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number } },
|
||||
>primary : { __typename?: "Feature" | undefined; } & { colors: { light: number;}; }
|
||||
>__typename : "Feature" | undefined
|
||||
>colors : { light: number; }
|
||||
>light : number
|
||||
|
||||
};
|
||||
|
||||
type Query = T1 & T2;
|
||||
>Query : T1 & T2
|
||||
|
||||
const response: Query = {
|
||||
>response : Query
|
||||
>{ primary: { colors: { light: 1, dark: 3, }, },} : { primary: { colors: { light: number; dark: number; }; }; }
|
||||
|
||||
primary: {
|
||||
>primary : { colors: { light: number; dark: number; }; }
|
||||
>{ colors: { light: 1, dark: 3, }, } : { colors: { light: number; dark: number; }; }
|
||||
|
||||
colors: {
|
||||
>colors : { light: number; dark: number; }
|
||||
>{ light: 1, dark: 3, } : { light: number; dark: number; }
|
||||
|
||||
light: 1,
|
||||
>light : number
|
||||
>1 : 1
|
||||
|
||||
dark: 3,
|
||||
>dark : number
|
||||
>3 : 3
|
||||
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -5,12 +5,8 @@ tests/cases/conformance/types/thisType/unionThisTypeInFunctions.ts(10,5): error
|
||||
Type '(this: Real, n: number) => void' is not assignable to type '(this: Fake, n: number) => void'.
|
||||
The 'this' types of each signature are incompatible.
|
||||
Type 'Fake' is not assignable to type 'Real'.
|
||||
Types of property 'method' are incompatible.
|
||||
Type '(this: Fake, n: number) => void' is not assignable to type '(this: Real, n: number) => void'.
|
||||
The 'this' types of each signature are incompatible.
|
||||
Type 'Real' is not assignable to type 'Fake'.
|
||||
Types of property 'data' are incompatible.
|
||||
Type 'string' is not assignable to type 'number'.
|
||||
Types of property 'data' are incompatible.
|
||||
Type 'number' is not assignable to type 'string'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/thisType/unionThisTypeInFunctions.ts (1 errors) ====
|
||||
@@ -32,11 +28,7 @@ tests/cases/conformance/types/thisType/unionThisTypeInFunctions.ts(10,5): error
|
||||
!!! error TS2684: Type '(this: Real, n: number) => void' is not assignable to type '(this: Fake, n: number) => void'.
|
||||
!!! error TS2684: The 'this' types of each signature are incompatible.
|
||||
!!! error TS2684: Type 'Fake' is not assignable to type 'Real'.
|
||||
!!! error TS2684: Types of property 'method' are incompatible.
|
||||
!!! error TS2684: Type '(this: Fake, n: number) => void' is not assignable to type '(this: Real, n: number) => void'.
|
||||
!!! error TS2684: The 'this' types of each signature are incompatible.
|
||||
!!! error TS2684: Type 'Real' is not assignable to type 'Fake'.
|
||||
!!! error TS2684: Types of property 'data' are incompatible.
|
||||
!!! error TS2684: Type 'string' is not assignable to type 'number'.
|
||||
!!! error TS2684: Types of property 'data' are incompatible.
|
||||
!!! error TS2684: Type 'number' is not assignable to type 'string'.
|
||||
}
|
||||
|
||||
@@ -42,3 +42,24 @@ const foo2: Unrelated & { variables: VariablesA & VariablesB } = {
|
||||
overrides: false // Error
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified repro from #52252
|
||||
|
||||
type T1 = {
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number, dark: number } },
|
||||
};
|
||||
|
||||
type T2 = {
|
||||
primary: { __typename?: 'Feature' } & { colors: { light: number } },
|
||||
};
|
||||
|
||||
type Query = T1 & T2;
|
||||
|
||||
const response: Query = {
|
||||
primary: {
|
||||
colors: {
|
||||
light: 1,
|
||||
dark: 3,
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in new issue
Block a user