From d50575f84b5b152424e796a35351989ff09062e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 8 Oct 2023 11:36:23 +0200 Subject: [PATCH] Fixed a relationship check when apparent type of mapped type expands to a union --- src/compiler/checker.ts | 4 ++ .../intersectionSatisfiesConstraint.types | 4 +- .../keyofAndIndexedAccessErrors.errors.txt | 12 +++-- ...appedTypeUnionArrayTupleConstraint.symbols | 44 +++++++++++++++++++ .../mappedTypeUnionArrayTupleConstraint.types | 25 +++++++++++ ...onstrainTupleTreatedAsArrayLike.errors.txt | 2 + .../mappedTypeUnionArrayTupleConstraint.ts | 15 +++++++ 7 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/mappedTypeUnionArrayTupleConstraint.symbols create mode 100644 tests/baselines/reference/mappedTypeUnionArrayTupleConstraint.types create mode 100644 tests/cases/compiler/mappedTypeUnionArrayTupleConstraint.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2c9344679abd8..38b0c86a9e8bd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22373,7 +22373,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const sourceIsPrimitive = !!(sourceFlags & TypeFlags.Primitive); if (relation !== identityRelation) { + const originalSource = source; source = getApparentType(source); + if (!(originalSource.flags & TypeFlags.Union) && source.flags & TypeFlags.Union && (result = unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState))) { + return result; + } sourceFlags = source.flags; } else if (isGenericMappedType(source)) { diff --git a/tests/baselines/reference/intersectionSatisfiesConstraint.types b/tests/baselines/reference/intersectionSatisfiesConstraint.types index 49e5eda86e073..9ab79847f6c54 100644 --- a/tests/baselines/reference/intersectionSatisfiesConstraint.types +++ b/tests/baselines/reference/intersectionSatisfiesConstraint.types @@ -29,9 +29,9 @@ const myFirstFunction = (param1: T) >3 : 3 mySecondFunction(newParam) ->mySecondFunction(newParam) : { commonProperty: number; otherProperty: number; } +>mySecondFunction(newParam) : T & { otherProperty: number; } >mySecondFunction : (newParam: T) => T ->newParam : (FirstInterface | SecondInterface) & { otherProperty: number; } +>newParam : T & { otherProperty: number; } } const mySecondFunction = (newParam: T) => { diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt index 803940642b128..e086e915a0d12 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt @@ -42,8 +42,10 @@ keyofAndIndexedAccessErrors.ts(103,9): error TS2322: Type 'Extract' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. Type 'string & keyof T' is not assignable to type 'K'. 'string & keyof T' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. - Type 'string' is not assignable to type 'K'. - 'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. + Type 'String & string' is not assignable to type 'K'. + 'String & string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. + Type 'string' is not assignable to type 'K'. + 'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. keyofAndIndexedAccessErrors.ts(105,9): error TS2322: Type 'T[Extract]' is not assignable to type 'T[K]'. Type 'Extract' is not assignable to type 'K'. 'Extract' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. @@ -249,8 +251,10 @@ keyofAndIndexedAccessErrors.ts(165,5): error TS2322: Type 'number' is not assign !!! error TS2322: 'Extract' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. !!! error TS2322: Type 'string & keyof T' is not assignable to type 'K'. !!! error TS2322: 'string & keyof T' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. -!!! error TS2322: Type 'string' is not assignable to type 'K'. -!!! error TS2322: 'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. +!!! error TS2322: Type 'String & string' is not assignable to type 'K'. +!!! error TS2322: 'String & string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. +!!! error TS2322: Type 'string' is not assignable to type 'K'. +!!! error TS2322: 'string' is assignable to the constraint of type 'K', but 'K' could be instantiated with a different subtype of constraint 'string'. t[key] = tk; // ok, T[K] ==> T[keyof T] tk = t[key]; // error, T[keyof T] =/=> T[K] ~~ diff --git a/tests/baselines/reference/mappedTypeUnionArrayTupleConstraint.symbols b/tests/baselines/reference/mappedTypeUnionArrayTupleConstraint.symbols new file mode 100644 index 0000000000000..4f607e7f64cef --- /dev/null +++ b/tests/baselines/reference/mappedTypeUnionArrayTupleConstraint.symbols @@ -0,0 +1,44 @@ +//// [tests/cases/compiler/mappedTypeUnionArrayTupleConstraint.ts] //// + +=== mappedTypeUnionArrayTupleConstraint.ts === +// https://github.com/microsoft/TypeScript/issues/56018 + +type Renamed = readonly ({ [k: PropertyKey]: string } | undefined)[]; +>Renamed : Symbol(Renamed, Decl(mappedTypeUnionArrayTupleConstraint.ts, 0, 0)) +>k : Symbol(k, Decl(mappedTypeUnionArrayTupleConstraint.ts, 2, 28)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) + +type Foo = +>Foo : Symbol(Foo, Decl(mappedTypeUnionArrayTupleConstraint.ts, 2, 69)) +>T : Symbol(T, Decl(mappedTypeUnionArrayTupleConstraint.ts, 4, 9)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) +>Renamed : Symbol(Renamed, Decl(mappedTypeUnionArrayTupleConstraint.ts, 0, 0)) + + T extends Renamed ? GetKeys> : Required; +>T : Symbol(T, Decl(mappedTypeUnionArrayTupleConstraint.ts, 4, 9)) +>Renamed : Symbol(Renamed, Decl(mappedTypeUnionArrayTupleConstraint.ts, 0, 0)) +>GetKeys : Symbol(GetKeys, Decl(mappedTypeUnionArrayTupleConstraint.ts, 5, 57)) +>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(mappedTypeUnionArrayTupleConstraint.ts, 4, 9)) +>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(mappedTypeUnionArrayTupleConstraint.ts, 4, 9)) + +type GetKeys = { [K in keyof R]: keyof R[K] }; +>GetKeys : Symbol(GetKeys, Decl(mappedTypeUnionArrayTupleConstraint.ts, 5, 57)) +>R : Symbol(R, Decl(mappedTypeUnionArrayTupleConstraint.ts, 7, 13)) +>Renamed : Symbol(Renamed, Decl(mappedTypeUnionArrayTupleConstraint.ts, 0, 0)) +>K : Symbol(K, Decl(mappedTypeUnionArrayTupleConstraint.ts, 7, 37)) +>R : Symbol(R, Decl(mappedTypeUnionArrayTupleConstraint.ts, 7, 13)) +>R : Symbol(R, Decl(mappedTypeUnionArrayTupleConstraint.ts, 7, 13)) +>K : Symbol(K, Decl(mappedTypeUnionArrayTupleConstraint.ts, 7, 37)) + +// usage +type A = Foo<["a"?]>; +>A : Symbol(A, Decl(mappedTypeUnionArrayTupleConstraint.ts, 7, 65)) +>Foo : Symbol(Foo, Decl(mappedTypeUnionArrayTupleConstraint.ts, 2, 69)) + +type B = Foo<[{ a?: "b" }]>; +>B : Symbol(B, Decl(mappedTypeUnionArrayTupleConstraint.ts, 10, 21)) +>Foo : Symbol(Foo, Decl(mappedTypeUnionArrayTupleConstraint.ts, 2, 69)) +>a : Symbol(a, Decl(mappedTypeUnionArrayTupleConstraint.ts, 11, 15)) + diff --git a/tests/baselines/reference/mappedTypeUnionArrayTupleConstraint.types b/tests/baselines/reference/mappedTypeUnionArrayTupleConstraint.types new file mode 100644 index 0000000000000..4436012e387d9 --- /dev/null +++ b/tests/baselines/reference/mappedTypeUnionArrayTupleConstraint.types @@ -0,0 +1,25 @@ +//// [tests/cases/compiler/mappedTypeUnionArrayTupleConstraint.ts] //// + +=== mappedTypeUnionArrayTupleConstraint.ts === +// https://github.com/microsoft/TypeScript/issues/56018 + +type Renamed = readonly ({ [k: PropertyKey]: string } | undefined)[]; +>Renamed : readonly ({ [k: string]: string; [k: number]: string; [k: symbol]: string; } | undefined)[] +>k : PropertyKey + +type Foo = +>Foo : Foo + + T extends Renamed ? GetKeys> : Required; + +type GetKeys = { [K in keyof R]: keyof R[K] }; +>GetKeys : GetKeys + +// usage +type A = Foo<["a"?]>; +>A : ["a"] + +type B = Foo<[{ a?: "b" }]>; +>B : ["a"] +>a : "b" | undefined + diff --git a/tests/baselines/reference/mappedTypeUnionConstrainTupleTreatedAsArrayLike.errors.txt b/tests/baselines/reference/mappedTypeUnionConstrainTupleTreatedAsArrayLike.errors.txt index 508c1eab98f75..0e69f53318843 100644 --- a/tests/baselines/reference/mappedTypeUnionConstrainTupleTreatedAsArrayLike.errors.txt +++ b/tests/baselines/reference/mappedTypeUnionConstrainTupleTreatedAsArrayLike.errors.txt @@ -1,4 +1,5 @@ mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts(9,9): error TS2322: Type 'HomomorphicMappedType' is not assignable to type 'any[]'. + The type 'readonly [boolean]' is 'readonly' and cannot be assigned to the mutable type 'any[]'. ==== mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts (1 errors) ==== @@ -13,6 +14,7 @@ mappedTypeUnionConstrainTupleTreatedAsArrayLike.ts(9,9): error TS2322: Type 'Hom const arr: any[] = [] as HomomorphicMappedType // error ~~~ !!! error TS2322: Type 'HomomorphicMappedType' is not assignable to type 'any[]'. +!!! error TS2322: The type 'readonly [boolean]' is 'readonly' and cannot be assigned to the mutable type 'any[]'. const arr2: readonly any[] = [] as HomomorphicMappedType } \ No newline at end of file diff --git a/tests/cases/compiler/mappedTypeUnionArrayTupleConstraint.ts b/tests/cases/compiler/mappedTypeUnionArrayTupleConstraint.ts new file mode 100644 index 0000000000000..7b09846f1190c --- /dev/null +++ b/tests/cases/compiler/mappedTypeUnionArrayTupleConstraint.ts @@ -0,0 +1,15 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/56018 + +type Renamed = readonly ({ [k: PropertyKey]: string } | undefined)[]; + +type Foo = + T extends Renamed ? GetKeys> : Required; + +type GetKeys = { [K in keyof R]: keyof R[K] }; + +// usage +type A = Foo<["a"?]>; +type B = Foo<[{ a?: "b" }]>;