diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cfd83efeb31aa..9c708d995f48f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7890,7 +7890,7 @@ namespace ts { } function getSimplifiedTypeOrConstraint(type: Type) { - const simplified = getSimplifiedType(type, /*writing*/ false); + const simplified = getSimplifiedType(type); return simplified !== type ? simplified : getConstraintOfType(type); } @@ -7935,7 +7935,7 @@ namespace ts { // a union - once negated types exist and are applied to the conditional false branch, this "constraint" // likely doesn't need to exist. if (type.root.isDistributive && type.restrictiveInstantiation !== type) { - const simplified = getSimplifiedType(type.checkType, /*writing*/ false); + const simplified = getSimplifiedType(type.checkType); const constraint = simplified === type.checkType ? getConstraintOfType(simplified) : simplified; if (constraint && constraint !== type.checkType) { const mapper = makeUnaryTypeMapper(type.root.checkType, constraint); @@ -8037,7 +8037,7 @@ namespace ts { return t.immediateBaseConstraint = noConstraintType; } constraintDepth++; - let result = computeBaseConstraint(getSimplifiedType(t, /*writing*/ false)); + let result = computeBaseConstraint(getSimplifiedType(t)); constraintDepth--; if (!popTypeResolution()) { if (t.flags & TypeFlags.TypeParameter) { @@ -10253,9 +10253,23 @@ namespace ts { const type = createType(TypeFlags.IndexedAccess); type.objectType = objectType; type.indexType = indexType; + type.writing = false; return type; } + function getWritingIndexedAccessType(type: IndexedAccessType) { + const id = "w," + type.objectType.id + "," + type.indexType.id; + let writeType = indexedAccessTypes.get(id); + if (!writeType) { + writeType = createType(TypeFlags.IndexedAccess); + writeType.objectType = type.objectType; + writeType.indexType = type.indexType; + writeType.writing = true; + indexedAccessTypes.set(id, writeType); + } + return writeType; + } + /** * Returns if a type is or consists of a JSLiteral object type * In addition to objects which are directly literals, @@ -10462,9 +10476,9 @@ namespace ts { return !!(type.flags & TypeFlags.TypeParameter && (type).isThisType); } - function getSimplifiedType(type: Type, writing: boolean): Type { - return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type, writing) : - type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type, writing) : + function getSimplifiedType(type: Type): Type { + return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type) : + type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type) : type; } @@ -10473,7 +10487,7 @@ namespace ts { // (T | U)[K] -> T[K] & U[K] (writing) // (T & U)[K] -> T[K] & U[K] if (objectType.flags & TypeFlags.UnionOrIntersection) { - const types = map((objectType as UnionOrIntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType), writing)); + const types = map((objectType as UnionOrIntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType))); return objectType.flags & TypeFlags.Intersection || writing ? getIntersectionType(types) : getUnionType(types); } } @@ -10482,7 +10496,7 @@ namespace ts { // T[A | B] -> T[A] | T[B] (reading) // T[A | B] -> T[A] & T[B] (writing) if (indexType.flags & TypeFlags.Union) { - const types = map((indexType as UnionType).types, t => getSimplifiedType(getIndexedAccessType(objectType, t), writing)); + const types = map((indexType as UnionType).types, t => getSimplifiedType(getIndexedAccessType(objectType, t))); return writing ? getIntersectionType(types) : getUnionType(types); } } @@ -10490,7 +10504,8 @@ namespace ts { // Transform an indexed access to a simpler form, if possible. Return the simpler form, or return // the type itself if no transformation is possible. The writing flag indicates that the type is // the target of an assignment. - function getSimplifiedIndexedAccessType(type: IndexedAccessType, writing: boolean): Type { + function getSimplifiedIndexedAccessType(type: IndexedAccessType): Type { + const writing = type.writing; const cache = writing ? "simplifiedForWriting" : "simplifiedForReading"; if (type[cache]) { return type[cache] === circularConstraintType ? type : type[cache]!; @@ -10498,8 +10513,8 @@ namespace ts { type[cache] = circularConstraintType; // We recursively simplify the object type as it may in turn be an indexed access type. For example, with // '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type. - const objectType = getSimplifiedType(type.objectType, writing); - const indexType = getSimplifiedType(type.indexType, writing); + const objectType = getSimplifiedType(type.objectType); + const indexType = getSimplifiedType(type.indexType); // T[A | B] -> T[A] | T[B] (reading) // T[A | B] -> T[A] & T[B] (writing) const distributedOverIndex = distributeObjectOverIndexType(objectType, indexType, writing); @@ -10523,12 +10538,12 @@ namespace ts { // that substitutes the index type for P. For example, for an index access { [P in K]: Box }[X], we // construct the type Box. if (isGenericMappedType(objectType)) { - return type[cache] = mapType(substituteIndexedMappedType(objectType, type.indexType), t => getSimplifiedType(t, writing)); + return type[cache] = mapType(substituteIndexedMappedType(objectType, type.indexType), t => getSimplifiedType(t)); } return type[cache] = type; } - function getSimplifiedConditionalType(type: ConditionalType, writing: boolean) { + function getSimplifiedConditionalType(type: ConditionalType) { const checkType = type.checkType; const extendsType = type.extendsType; const trueType = getTrueTypeFromConditionalType(type); @@ -10536,7 +10551,7 @@ namespace ts { // Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. if (falseType.flags & TypeFlags.Never && getActualTypeVariable(trueType) === getActualTypeVariable(checkType)) { if (checkType.flags & TypeFlags.Any || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true - return getSimplifiedType(trueType, writing); + return getSimplifiedType(trueType); } else if (isIntersectionEmpty(checkType, extendsType)) { // Always false return neverType; @@ -10547,7 +10562,7 @@ namespace ts { return neverType; } else if (checkType.flags & TypeFlags.Any || isIntersectionEmpty(checkType, extendsType)) { // Always false - return getSimplifiedType(falseType, writing); + return getSimplifiedType(falseType); } } return type; @@ -10589,7 +10604,7 @@ namespace ts { return objectType; } // Defer the operation by creating an indexed access type. - const id = objectType.id + "," + indexType.id; + const id = "r," + objectType.id + "," + indexType.id; let type = indexedAccessTypes.get(id); if (!type) { indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType)); @@ -12812,10 +12827,10 @@ namespace ts { target = (target).typeVariable; } if (source.flags & TypeFlags.Simplifiable) { - source = getSimplifiedType(source, /*writing*/ false); + source = getSimplifiedType(source); } if (target.flags & TypeFlags.Simplifiable) { - target = getSimplifiedType(target, /*writing*/ true); + target = getSimplifiedType(target); } // Try to see if we're relating something like `Foo` -> `Bar | null | undefined`. @@ -15653,16 +15668,16 @@ namespace ts { } else { // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine - const simplified = getSimplifiedType(target, /*writing*/ false); + const simplified = getSimplifiedType(target); if (simplified !== target) { invokeOnce(source, simplified, inferFromTypes); } else if (target.flags & TypeFlags.IndexedAccess) { - const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false); + const indexType = getSimplifiedType((target as IndexedAccessType).indexType); // Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider // that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can. if (indexType.flags & TypeFlags.Instantiable) { - const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false); + const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType), indexType, (target as IndexedAccessType).writing); if (simplified && simplified !== target) { invokeOnce(source, simplified, inferFromTypes); } @@ -24738,6 +24753,7 @@ namespace ts { if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access) && (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) { // to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported + leftType = leftType.flags & TypeFlags.IndexedAccess ? getWritingIndexedAccessType(leftType) : leftType; checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2c8a882c1640a..24844d9a417ea 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4375,6 +4375,7 @@ namespace ts { export interface IndexedAccessType extends InstantiableType { objectType: Type; indexType: Type; + writing: boolean; constraint?: Type; simplifiedForReading?: Type; simplifiedForWriting?: Type; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index bffe2114d19b5..3ebf9de489ca1 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2383,6 +2383,7 @@ declare namespace ts { export interface IndexedAccessType extends InstantiableType { objectType: Type; indexType: Type; + writing: boolean; constraint?: Type; simplifiedForReading?: Type; simplifiedForWriting?: Type; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 457a1acb227aa..02570f212fa3c 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2383,6 +2383,7 @@ declare namespace ts { export interface IndexedAccessType extends InstantiableType { objectType: Type; indexType: Type; + writing: boolean; constraint?: Type; simplifiedForReading?: Type; simplifiedForWriting?: Type; diff --git a/tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt b/tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt index af63c18523d6a..ceee293b747d9 100644 --- a/tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt +++ b/tests/baselines/reference/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.errors.txt @@ -1,24 +1,21 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts(33,5): error TS2322: Type '{ type: T; localChannelId: string; }' is not assignable to type 'NewChannel | ChannelOfType>'. Type '{ type: T; localChannelId: string; }' is not assignable to type 'Pick | ChannelOfType, "type">'. Types of property 'type' are incompatible. - Type 'T' is not assignable to type 'ChannelOfType["type"] & ChannelOfType["type"]'. - Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"] & ChannelOfType["type"]'. - Type '"text"' is not assignable to type 'ChannelOfType["type"] & ChannelOfType["type"]'. - Type '"text"' is not assignable to type 'ChannelOfType["type"]'. - Type 'T' is not assignable to type 'ChannelOfType["type"]'. - Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"]'. - Type '"text"' is not assignable to type 'ChannelOfType["type"]'. - Type '"text"' is not assignable to type 'T & "text"'. - Type '"text"' is not assignable to type 'T'. - '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. - Type 'T' is not assignable to type 'T & "text"'. - Type '"text" | "email"' is not assignable to type 'T & "text"'. - Type '"text"' is not assignable to type 'T & "text"'. - Type '"text"' is not assignable to type 'T'. - '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. - Type 'T' is not assignable to type '"text"'. - Type '"text" | "email"' is not assignable to type '"text"'. - Type '"email"' is not assignable to type '"text"'. + Type 'T' is not assignable to type 'ChannelOfType["type"] | ChannelOfType["type"]'. + Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"] | ChannelOfType["type"]'. + Type '"text"' is not assignable to type 'ChannelOfType["type"] | ChannelOfType["type"]'. + Type 'T' is not assignable to type 'ChannelOfType["type"]'. + Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"]'. + Type '"text"' is not assignable to type 'ChannelOfType["type"]'. + Type '"text"' is not assignable to type 'T & "email"'. + Type '"text"' is not assignable to type 'T'. + '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. + Type 'T' is not assignable to type 'T & "email"'. + Type '"text" | "email"' is not assignable to type 'T & "email"'. + Type '"text"' is not assignable to type 'T & "email"'. + Type '"text"' is not assignable to type 'T'. + '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. + Type 'T' is not assignable to type '"email"'. ==== tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts (1 errors) ==== @@ -59,24 +56,21 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t !!! error TS2322: Type '{ type: T; localChannelId: string; }' is not assignable to type 'NewChannel | ChannelOfType>'. !!! error TS2322: Type '{ type: T; localChannelId: string; }' is not assignable to type 'Pick | ChannelOfType, "type">'. !!! error TS2322: Types of property 'type' are incompatible. -!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType["type"] & ChannelOfType["type"]'. -!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"] & ChannelOfType["type"]'. -!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType["type"] & ChannelOfType["type"]'. -!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType["type"]'. -!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType["type"]'. -!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"]'. -!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType["type"]'. -!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'. -!!! error TS2322: Type '"text"' is not assignable to type 'T'. -!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. -!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'. -!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'. -!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'. -!!! error TS2322: Type '"text"' is not assignable to type 'T'. -!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. -!!! error TS2322: Type 'T' is not assignable to type '"text"'. -!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'. -!!! error TS2322: Type '"email"' is not assignable to type '"text"'. +!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType["type"] | ChannelOfType["type"]'. +!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"] | ChannelOfType["type"]'. +!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType["type"] | ChannelOfType["type"]'. +!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType["type"]'. +!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType["type"]'. +!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType["type"]'. +!!! error TS2322: Type '"text"' is not assignable to type 'T & "email"'. +!!! error TS2322: Type '"text"' is not assignable to type 'T'. +!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. +!!! error TS2322: Type 'T' is not assignable to type 'T & "email"'. +!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "email"'. +!!! error TS2322: Type '"text"' is not assignable to type 'T & "email"'. +!!! error TS2322: Type '"text"' is not assignable to type 'T'. +!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'. +!!! error TS2322: Type 'T' is not assignable to type '"email"'. } const newTextChannel = makeNewChannel('text'); diff --git a/tests/baselines/reference/relatingToIndexedAccessTypesWithoutMutation.js b/tests/baselines/reference/relatingToIndexedAccessTypesWithoutMutation.js new file mode 100644 index 0000000000000..c1b99a4f7b5a3 --- /dev/null +++ b/tests/baselines/reference/relatingToIndexedAccessTypesWithoutMutation.js @@ -0,0 +1,97 @@ +//// [relatingToIndexedAccessTypesWithoutMutation.ts] +// @strict + +// Repro of #32816 + +type Target = T extends null ? null : T; +type Target2 = { "one": null, "two": T }[T extends null ? "one" : "two"]; +type Target3 = [null, T][T extends null ? 0 : 1]; + +function tst() { + // These two pass as expected: + const case0: Target2 = 1 as any as Target2; + const case1: { prop: Target } = 1 as any as { prop: Target }; + const case2: { prop: Target2 } = 1 as any as { prop: Target2 }; + const case3: { prop: Target3 } = 1 as any as { prop: Target3 }; + + // These two fail as expected: + const case4: { prop: Target2 } = 1 as any as { prop: Target2 }; + const case5: { prop: Target3 } = 1 as any as { prop: Target3 }; + + // These two are expected to pass, but fail: + const case6: { prop: Target2 } = 1 as any as { prop: Target2 }; + const case7: { prop: Target3 } = 1 as any as { prop: Target3 }; +} + +// Repro of #31833 + +type Foo1 = { + type: 'foo1'; + extra: number; +}; + +type Foo2 = { + type: 'foo2'; + extra: string; +}; + +type Both = Foo1 | Foo2; + +type FooTypes = Both['type']; + +export type FooFromType = O extends { type: T } ? O : never; + +type FooExtraFromType = FooFromType['extra']; + +function fnWithFooExtra(type: T, extra: FooExtraFromType) { } + +type FnType = (type: T, extra: FooExtraFromType) => void; + +const fn: FnType = fnWithFooExtra; + +// You need the two aliases to avoid variance measurements. + +type A1 = < +T extends { x: number, y: string } | { x: boolean, y: number} +>( + x: T["x" | "y"] +) => void + +type A2 = < +T extends { x: number, y: string } | { x: boolean, y: number} +>( + x: T["x" | "y"] +) => void + +declare const a: A1; +let b: A2 = a; // no error + +type Obj = { x: number, y: string } | { x: boolean, y: number}; +function fun(l: { x: T["x" | "y"] }, r: { x: T["x" | "y"] }) { + l = r; +} + + +//// [relatingToIndexedAccessTypesWithoutMutation.js] +"use strict"; +// @strict +exports.__esModule = true; +function tst() { + // These two pass as expected: + var case0 = 1; + var case1 = 1; + var case2 = 1; + var case3 = 1; + // These two fail as expected: + var case4 = 1; + var case5 = 1; + // These two are expected to pass, but fail: + var case6 = 1; + var case7 = 1; +} +function fnWithFooExtra(type, extra) { } +var fn = fnWithFooExtra; +var b = a; // no error +function fun(l, r) { + l = r; +} diff --git a/tests/baselines/reference/relatingToIndexedAccessTypesWithoutMutation.symbols b/tests/baselines/reference/relatingToIndexedAccessTypesWithoutMutation.symbols new file mode 100644 index 0000000000000..63c5784c2fce0 --- /dev/null +++ b/tests/baselines/reference/relatingToIndexedAccessTypesWithoutMutation.symbols @@ -0,0 +1,248 @@ +=== tests/cases/compiler/relatingToIndexedAccessTypesWithoutMutation.ts === +// @strict + +// Repro of #32816 + +type Target = T extends null ? null : T; +>Target : Symbol(Target, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 0, 0)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 12)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 12)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 12)) + +type Target2 = { "one": null, "two": T }[T extends null ? "one" : "two"]; +>Target2 : Symbol(Target2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 43)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 13)) +>"one" : Symbol("one", Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 19)) +>"two" : Symbol("two", Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 32)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 13)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 13)) + +type Target3 = [null, T][T extends null ? 0 : 1]; +>Target3 : Symbol(Target3, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 76)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 6, 13)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 6, 13)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 6, 13)) + +function tst() { +>tst : Symbol(tst, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 6, 52)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) + + // These two pass as expected: + const case0: Target2 = 1 as any as Target2; +>case0 : Symbol(case0, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 10, 9)) +>Target2 : Symbol(Target2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 43)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) +>Target2 : Symbol(Target2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 43)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) + + const case1: { prop: Target } = 1 as any as { prop: Target }; +>case1 : Symbol(case1, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 11, 9)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 11, 18)) +>Target : Symbol(Target, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 0, 0)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 11, 59)) +>Target : Symbol(Target, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 0, 0)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) + + const case2: { prop: Target2 } = 1 as any as { prop: Target2 }; +>case2 : Symbol(case2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 12, 9)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 12, 18)) +>Target2 : Symbol(Target2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 43)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 12, 53)) +>Target2 : Symbol(Target2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 43)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) + + const case3: { prop: Target3 } = 1 as any as { prop: Target3 }; +>case3 : Symbol(case3, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 13, 9)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 13, 18)) +>Target3 : Symbol(Target3, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 76)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 13, 53)) +>Target3 : Symbol(Target3, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 76)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) + + // These two fail as expected: + const case4: { prop: Target2 } = 1 as any as { prop: Target2 }; +>case4 : Symbol(case4, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 16, 9)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 16, 18)) +>Target2 : Symbol(Target2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 43)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 16, 53)) +>Target2 : Symbol(Target2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 43)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) + + const case5: { prop: Target3 } = 1 as any as { prop: Target3 }; +>case5 : Symbol(case5, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 17, 9)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 17, 18)) +>Target3 : Symbol(Target3, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 76)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 17, 53)) +>Target3 : Symbol(Target3, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 76)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) + + // These two are expected to pass, but fail: + const case6: { prop: Target2 } = 1 as any as { prop: Target2 }; +>case6 : Symbol(case6, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 20, 9)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 20, 18)) +>Target2 : Symbol(Target2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 43)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 20, 60)) +>Target2 : Symbol(Target2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 4, 43)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) + + const case7: { prop: Target3 } = 1 as any as { prop: Target3 }; +>case7 : Symbol(case7, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 21, 9)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 21, 18)) +>Target3 : Symbol(Target3, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 76)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) +>prop : Symbol(prop, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 21, 60)) +>Target3 : Symbol(Target3, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 5, 76)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 8, 13)) +} + +// Repro of #31833 + +type Foo1 = { +>Foo1 : Symbol(Foo1, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 22, 1)) + + type: 'foo1'; +>type : Symbol(type, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 26, 13)) + + extra: number; +>extra : Symbol(extra, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 27, 15)) + +}; + +type Foo2 = { +>Foo2 : Symbol(Foo2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 29, 2)) + + type: 'foo2'; +>type : Symbol(type, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 31, 13)) + + extra: string; +>extra : Symbol(extra, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 32, 15)) + +}; + +type Both = Foo1 | Foo2; +>Both : Symbol(Both, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 34, 2)) +>Foo1 : Symbol(Foo1, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 22, 1)) +>Foo2 : Symbol(Foo2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 29, 2)) + +type FooTypes = Both['type']; +>FooTypes : Symbol(FooTypes, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 36, 24)) +>Both : Symbol(Both, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 34, 2)) + +export type FooFromType = O extends { type: T } ? O : never; +>FooFromType : Symbol(FooFromType, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 38, 29)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 40, 24)) +>FooTypes : Symbol(FooTypes, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 36, 24)) +>O : Symbol(O, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 40, 43)) +>Both : Symbol(Both, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 34, 2)) +>Both : Symbol(Both, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 34, 2)) +>O : Symbol(O, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 40, 43)) +>type : Symbol(type, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 40, 80)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 40, 24)) +>O : Symbol(O, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 40, 43)) + +type FooExtraFromType = FooFromType['extra']; +>FooExtraFromType : Symbol(FooExtraFromType, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 40, 103)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 42, 22)) +>FooTypes : Symbol(FooTypes, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 36, 24)) +>FooFromType : Symbol(FooFromType, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 38, 29)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 42, 22)) + +function fnWithFooExtra(type: T, extra: FooExtraFromType) { } +>fnWithFooExtra : Symbol(fnWithFooExtra, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 42, 68)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 44, 24)) +>FooTypes : Symbol(FooTypes, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 36, 24)) +>type : Symbol(type, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 44, 44)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 44, 24)) +>extra : Symbol(extra, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 44, 52)) +>FooExtraFromType : Symbol(FooExtraFromType, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 40, 103)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 44, 24)) + +type FnType = (type: T, extra: FooExtraFromType) => void; +>FnType : Symbol(FnType, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 44, 84)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 46, 15)) +>FooTypes : Symbol(FooTypes, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 36, 24)) +>type : Symbol(type, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 46, 35)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 46, 15)) +>extra : Symbol(extra, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 46, 43)) +>FooExtraFromType : Symbol(FooExtraFromType, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 40, 103)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 46, 15)) + +const fn: FnType = fnWithFooExtra; +>fn : Symbol(fn, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 48, 5)) +>FnType : Symbol(FnType, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 44, 84)) +>fnWithFooExtra : Symbol(fnWithFooExtra, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 42, 68)) + +// You need the two aliases to avoid variance measurements. + +type A1 = < +>A1 : Symbol(A1, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 48, 34)) + +T extends { x: number, y: string } | { x: boolean, y: number} +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 52, 11)) +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 53, 11)) +>y : Symbol(y, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 53, 22)) +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 53, 38)) +>y : Symbol(y, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 53, 50)) + +>( + x: T["x" | "y"] +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 54, 2)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 52, 11)) + +) => void + +type A2 = < +>A2 : Symbol(A2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 56, 9)) + +T extends { x: number, y: string } | { x: boolean, y: number} +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 58, 11)) +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 59, 11)) +>y : Symbol(y, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 59, 22)) +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 59, 38)) +>y : Symbol(y, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 59, 50)) + +>( + x: T["x" | "y"] +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 60, 2)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 58, 11)) + +) => void + +declare const a: A1; +>a : Symbol(a, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 64, 13)) +>A1 : Symbol(A1, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 48, 34)) + +let b: A2 = a; // no error +>b : Symbol(b, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 65, 3)) +>A2 : Symbol(A2, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 56, 9)) +>a : Symbol(a, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 64, 13)) + +type Obj = { x: number, y: string } | { x: boolean, y: number}; +>Obj : Symbol(Obj, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 65, 14)) +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 67, 12)) +>y : Symbol(y, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 67, 23)) +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 67, 39)) +>y : Symbol(y, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 67, 51)) + +function fun(l: { x: T["x" | "y"] }, r: { x: T["x" | "y"] }) { +>fun : Symbol(fun, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 67, 63)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 68, 13)) +>Obj : Symbol(Obj, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 65, 14)) +>l : Symbol(l, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 68, 28)) +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 68, 32)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 68, 13)) +>r : Symbol(r, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 68, 51)) +>x : Symbol(x, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 68, 56)) +>T : Symbol(T, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 68, 13)) + + l = r; +>l : Symbol(l, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 68, 28)) +>r : Symbol(r, Decl(relatingToIndexedAccessTypesWithoutMutation.ts, 68, 51)) +} + diff --git a/tests/baselines/reference/relatingToIndexedAccessTypesWithoutMutation.types b/tests/baselines/reference/relatingToIndexedAccessTypesWithoutMutation.types new file mode 100644 index 0000000000000..51d56a9922299 --- /dev/null +++ b/tests/baselines/reference/relatingToIndexedAccessTypesWithoutMutation.types @@ -0,0 +1,211 @@ +=== tests/cases/compiler/relatingToIndexedAccessTypesWithoutMutation.ts === +// @strict + +// Repro of #32816 + +type Target = T extends null ? null : T; +>Target : Target +>null : null +>null : null + +type Target2 = { "one": null, "two": T }[T extends null ? "one" : "two"]; +>Target2 : { "one": null; "two": T; }[T extends null ? "one" : "two"] +>"one" : null +>null : null +>"two" : T +>null : null + +type Target3 = [null, T][T extends null ? 0 : 1]; +>Target3 : [null, T][T extends null ? 0 : 1] +>null : null +>null : null + +function tst() { +>tst : () => void + + // These two pass as expected: + const case0: Target2 = 1 as any as Target2; +>case0 : { "one": null; "two": T; }[T extends null ? "one" : "two"] +>null : null +>1 as any as Target2 : { "one": null; "two": T; }[T extends null ? "one" : "two"] +>1 as any : any +>1 : 1 +>null : null + + const case1: { prop: Target } = 1 as any as { prop: Target }; +>case1 : { prop: Target; } +>prop : Target +>null : null +>1 as any as { prop: Target } : { prop: Target; } +>1 as any : any +>1 : 1 +>prop : Target +>null : null + + const case2: { prop: Target2 } = 1 as any as { prop: Target2 }; +>case2 : { prop: { "one": null; "two": T; }[T extends null ? "one" : "two"]; } +>prop : { "one": null; "two": T; }[T extends null ? "one" : "two"] +>1 as any as { prop: Target2 } : { prop: { "one": null; "two": T; }[T extends null ? "one" : "two"]; } +>1 as any : any +>1 : 1 +>prop : { "one": null; "two": T; }[T extends null ? "one" : "two"] + + const case3: { prop: Target3 } = 1 as any as { prop: Target3 }; +>case3 : { prop: [null, T][T extends null ? 0 : 1]; } +>prop : [null, T][T extends null ? 0 : 1] +>1 as any as { prop: Target3 } : { prop: [null, T][T extends null ? 0 : 1]; } +>1 as any : any +>1 : 1 +>prop : [null, T][T extends null ? 0 : 1] + + // These two fail as expected: + const case4: { prop: Target2 } = 1 as any as { prop: Target2 }; +>case4 : { prop: { "one": null; "two": T; }[T extends null ? "one" : "two"]; } +>prop : { "one": null; "two": T; }[T extends null ? "one" : "two"] +>1 as any as { prop: Target2 } : { prop: { "one": null; "two": T; }[T extends null ? "one" : "two"]; } +>1 as any : any +>1 : 1 +>prop : { "one": null; "two": T; }[T extends null ? "one" : "two"] +>null : null + + const case5: { prop: Target3 } = 1 as any as { prop: Target3 }; +>case5 : { prop: [null, T][T extends null ? 0 : 1]; } +>prop : [null, T][T extends null ? 0 : 1] +>1 as any as { prop: Target3 } : { prop: [null, T][T extends null ? 0 : 1]; } +>1 as any : any +>1 : 1 +>prop : [null, T][T extends null ? 0 : 1] +>null : null + + // These two are expected to pass, but fail: + const case6: { prop: Target2 } = 1 as any as { prop: Target2 }; +>case6 : { prop: { "one": null; "two": T; }[T extends null ? "one" : "two"]; } +>prop : { "one": null; "two": T; }[T extends null ? "one" : "two"] +>null : null +>1 as any as { prop: Target2 } : { prop: { "one": null; "two": T; }[T extends null ? "one" : "two"]; } +>1 as any : any +>1 : 1 +>prop : { "one": null; "two": T; }[T extends null ? "one" : "two"] +>null : null + + const case7: { prop: Target3 } = 1 as any as { prop: Target3 }; +>case7 : { prop: [null, T][T extends null ? 0 : 1]; } +>prop : [null, T][T extends null ? 0 : 1] +>null : null +>1 as any as { prop: Target3 } : { prop: [null, T][T extends null ? 0 : 1]; } +>1 as any : any +>1 : 1 +>prop : [null, T][T extends null ? 0 : 1] +>null : null +} + +// Repro of #31833 + +type Foo1 = { +>Foo1 : Foo1 + + type: 'foo1'; +>type : "foo1" + + extra: number; +>extra : number + +}; + +type Foo2 = { +>Foo2 : Foo2 + + type: 'foo2'; +>type : "foo2" + + extra: string; +>extra : string + +}; + +type Both = Foo1 | Foo2; +>Both : Both + +type FooTypes = Both['type']; +>FooTypes : "foo1" | "foo2" + +export type FooFromType = O extends { type: T } ? O : never; +>FooFromType : FooFromType +>type : T + +type FooExtraFromType = FooFromType['extra']; +>FooExtraFromType : (FooFromType | FooFromType)["extra"] + +function fnWithFooExtra(type: T, extra: FooExtraFromType) { } +>fnWithFooExtra : (type: T, extra: (FooFromType | FooFromType)["extra"]) => void +>type : T +>extra : (FooFromType | FooFromType)["extra"] + +type FnType = (type: T, extra: FooExtraFromType) => void; +>FnType : FnType +>type : T +>extra : (FooFromType | FooFromType)["extra"] + +const fn: FnType = fnWithFooExtra; +>fn : FnType +>fnWithFooExtra : (type: T, extra: (FooFromType | FooFromType)["extra"]) => void + +// You need the two aliases to avoid variance measurements. + +type A1 = < +>A1 : A1 + +T extends { x: number, y: string } | { x: boolean, y: number} +>x : number +>y : string +>x : boolean +>y : number + +>( + x: T["x" | "y"] +>x : T["x" | "y"] + +) => void + +type A2 = < +>A2 : A2 + +T extends { x: number, y: string } | { x: boolean, y: number} +>x : number +>y : string +>x : boolean +>y : number + +>( + x: T["x" | "y"] +>x : T["x" | "y"] + +) => void + +declare const a: A1; +>a : A1 + +let b: A2 = a; // no error +>b : A2 +>a : A1 + +type Obj = { x: number, y: string } | { x: boolean, y: number}; +>Obj : Obj +>x : number +>y : string +>x : boolean +>y : number + +function fun(l: { x: T["x" | "y"] }, r: { x: T["x" | "y"] }) { +>fun : (l: { x: T["x" | "y"]; }, r: { x: T["x" | "y"]; }) => void +>l : { x: T["x" | "y"]; } +>x : T["x" | "y"] +>r : { x: T["x" | "y"]; } +>x : T["x" | "y"] + + l = r; +>l = r : { x: T["x" | "y"]; } +>l : { x: T["x" | "y"]; } +>r : { x: T["x" | "y"]; } +} + diff --git a/tests/cases/compiler/relatingToIndexedAccessTypesWithoutMutation.ts b/tests/cases/compiler/relatingToIndexedAccessTypesWithoutMutation.ts new file mode 100644 index 0000000000000..12cd0d03ac82f --- /dev/null +++ b/tests/cases/compiler/relatingToIndexedAccessTypesWithoutMutation.ts @@ -0,0 +1,71 @@ +// @strict + +// Repro of #32816 + +type Target = T extends null ? null : T; +type Target2 = { "one": null, "two": T }[T extends null ? "one" : "two"]; +type Target3 = [null, T][T extends null ? 0 : 1]; + +function tst() { + // These two pass as expected: + const case0: Target2 = 1 as any as Target2; + const case1: { prop: Target } = 1 as any as { prop: Target }; + const case2: { prop: Target2 } = 1 as any as { prop: Target2 }; + const case3: { prop: Target3 } = 1 as any as { prop: Target3 }; + + // These two fail as expected: + const case4: { prop: Target2 } = 1 as any as { prop: Target2 }; + const case5: { prop: Target3 } = 1 as any as { prop: Target3 }; + + // These two are expected to pass, but fail: + const case6: { prop: Target2 } = 1 as any as { prop: Target2 }; + const case7: { prop: Target3 } = 1 as any as { prop: Target3 }; +} + +// Repro of #31833 + +type Foo1 = { + type: 'foo1'; + extra: number; +}; + +type Foo2 = { + type: 'foo2'; + extra: string; +}; + +type Both = Foo1 | Foo2; + +type FooTypes = Both['type']; + +export type FooFromType = O extends { type: T } ? O : never; + +type FooExtraFromType = FooFromType['extra']; + +function fnWithFooExtra(type: T, extra: FooExtraFromType) { } + +type FnType = (type: T, extra: FooExtraFromType) => void; + +const fn: FnType = fnWithFooExtra; + +// You need the two aliases to avoid variance measurements. + +type A1 = < +T extends { x: number, y: string } | { x: boolean, y: number} +>( + x: T["x" | "y"] +) => void + +type A2 = < +T extends { x: number, y: string } | { x: boolean, y: number} +>( + x: T["x" | "y"] +) => void + +declare const a: A1; +let b: A2 = a; // no error + +type Obj = { x: number, y: string } | { x: boolean, y: number}; +function fun(l: { x: T["x" | "y"] }, r: { x: T["x" | "y"] }) { + l = r; +}