From fdc0b7ee235e04b2ddd97aba4d37687b51f8349e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 21 Jan 2024 11:29:28 +0100 Subject: [PATCH 1/3] Avoid creating redundant substitution types in non-generic contexts --- src/compiler/checker.ts | 2 +- ...exedAccessInConditionalTrueBranch1.symbols | 34 +++++++++++++++++++ ...ndexedAccessInConditionalTrueBranch1.types | 30 ++++++++++++++++ ...icIndexedAccessInConditionalTrueBranch1.ts | 17 ++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.symbols create mode 100644 tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.types create mode 100644 tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dd24f9be315ee..2db229389e087 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16173,7 +16173,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getSubstitutionType(baseType: Type, constraint: Type) { - return constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any ? + return constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any || !isGenericType(constraint) && !isGenericType(baseType) && isTypeAssignableTo(baseType, constraint) ? baseType : getOrCreateSubstitutionType(baseType, constraint); } diff --git a/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.symbols b/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.symbols new file mode 100644 index 0000000000000..3d6c7976dfa2c --- /dev/null +++ b/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.symbols @@ -0,0 +1,34 @@ +//// [tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts] //// + +=== nonGenericIndexedAccessInConditionalTrueBranch1.ts === +// https://github.com/microsoft/TypeScript/issues/57109 + +type A = { +>A : Symbol(A, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 0, 0)) + + k: symbol; +>k : Symbol(k, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 2, 10)) + +}; + +type B = "k" extends keyof A ? A["k"] : never; +>B : Symbol(B, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 4, 2)) +>A : Symbol(A, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 0, 0)) +>A : Symbol(A, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 0, 0)) + +type C = { +>C : Symbol(C, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 6, 46)) + + k: symbol; +>k : Symbol(k, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 8, 10)) + + other: boolean; +>other : Symbol(other, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 9, 12)) + +}; + +type D = "k" extends keyof C ? C["k"] : never; +>D : Symbol(D, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 11, 2)) +>C : Symbol(C, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 6, 46)) +>C : Symbol(C, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 6, 46)) + diff --git a/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.types b/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.types new file mode 100644 index 0000000000000..1aea4c05953dc --- /dev/null +++ b/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.types @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts] //// + +=== nonGenericIndexedAccessInConditionalTrueBranch1.ts === +// https://github.com/microsoft/TypeScript/issues/57109 + +type A = { +>A : { k: symbol; } + + k: symbol; +>k : symbol + +}; + +type B = "k" extends keyof A ? A["k"] : never; +>B : symbol + +type C = { +>C : { k: symbol; other: boolean; } + + k: symbol; +>k : symbol + + other: boolean; +>other : boolean + +}; + +type D = "k" extends keyof C ? C["k"] : never; +>D : symbol + diff --git a/tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts b/tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts new file mode 100644 index 0000000000000..9fc23c7c53af5 --- /dev/null +++ b/tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts @@ -0,0 +1,17 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/57109 + +type A = { + k: symbol; +}; + +type B = "k" extends keyof A ? A["k"] : never; + +type C = { + k: symbol; + other: boolean; +}; + +type D = "k" extends keyof C ? C["k"] : never; From fec72b575e0901346c0886dce1e807c31161c892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 27 Jun 2024 11:07:04 +0200 Subject: [PATCH 2/3] Unwrap substitution types early in `getPropertyTypeForIndexType` --- src/compiler/checker.ts | 5 ++++- .../nonGenericIndexedAccessInConditionalTrueBranch1.symbols | 5 +++++ .../nonGenericIndexedAccessInConditionalTrueBranch1.types | 4 ++++ .../nonGenericIndexedAccessInConditionalTrueBranch1.ts | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6d6ac7a75028f..fda7332dcccb8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16572,7 +16572,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getSubstitutionType(baseType: Type, constraint: Type) { - return constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any || !isGenericType(constraint) && !isGenericType(baseType) && isTypeAssignableTo(baseType, constraint) ? + return constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || baseType.flags & TypeFlags.Any ? baseType : getOrCreateSubstitutionType(baseType, constraint); } @@ -18538,6 +18538,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) { + if (indexType.flags & TypeFlags.Substitution) { + indexType = (indexType as SubstitutionType).baseType; + } const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; const propName = accessNode && isPrivateIdentifier(accessNode) ? undefined : getPropertyNameFromIndex(indexType, accessNode); diff --git a/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.symbols b/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.symbols index 3d6c7976dfa2c..b837974d2482f 100644 --- a/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.symbols +++ b/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.symbols @@ -32,3 +32,8 @@ type D = "k" extends keyof C ? C["k"] : never; >C : Symbol(C, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 6, 46)) >C : Symbol(C, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 6, 46)) +type E = "k" extends keyof C ? C["k" | "other"] : never; +>E : Symbol(E, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 13, 46)) +>C : Symbol(C, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 6, 46)) +>C : Symbol(C, Decl(nonGenericIndexedAccessInConditionalTrueBranch1.ts, 6, 46)) + diff --git a/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.types b/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.types index c8e173c025dd6..05f91d30491d7 100644 --- a/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.types +++ b/tests/baselines/reference/nonGenericIndexedAccessInConditionalTrueBranch1.types @@ -35,3 +35,7 @@ type D = "k" extends keyof C ? C["k"] : never; >D : symbol > : ^^^^^^ +type E = "k" extends keyof C ? C["k" | "other"] : never; +>E : boolean | symbol +> : ^^^^^^^^^^^^^^^^ + diff --git a/tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts b/tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts index 9fc23c7c53af5..ccfc45bc093c0 100644 --- a/tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts +++ b/tests/cases/compiler/nonGenericIndexedAccessInConditionalTrueBranch1.ts @@ -15,3 +15,4 @@ type C = { }; type D = "k" extends keyof C ? C["k"] : never; +type E = "k" extends keyof C ? C["k" | "other"] : never; From c647e5d81c3fb251b32dfeb9467ccb2c5bac3532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 13 Jul 2024 10:26:20 +0200 Subject: [PATCH 3/3] add extra test case --- .../substitutionTypeUsedAsIndex.symbols | 23 +++++++++++++++++++ .../substitutionTypeUsedAsIndex.types | 23 +++++++++++++++++++ .../compiler/substitutionTypeUsedAsIndex.ts | 12 ++++++++++ 3 files changed, 58 insertions(+) create mode 100644 tests/baselines/reference/substitutionTypeUsedAsIndex.symbols create mode 100644 tests/baselines/reference/substitutionTypeUsedAsIndex.types create mode 100644 tests/cases/compiler/substitutionTypeUsedAsIndex.ts diff --git a/tests/baselines/reference/substitutionTypeUsedAsIndex.symbols b/tests/baselines/reference/substitutionTypeUsedAsIndex.symbols new file mode 100644 index 0000000000000..bf6174ffdfc46 --- /dev/null +++ b/tests/baselines/reference/substitutionTypeUsedAsIndex.symbols @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/substitutionTypeUsedAsIndex.ts] //// + +=== substitutionTypeUsedAsIndex.ts === +// repro from https://github.com/microsoft/TypeScript/issues/54886 + +interface Dict_54886 { +>Dict_54886 : Symbol(Dict_54886, Decl(substitutionTypeUsedAsIndex.ts, 0, 0)) + + foo: 1; +>foo : Symbol(Dict_54886.foo, Decl(substitutionTypeUsedAsIndex.ts, 2, 22)) + + bar: 1; +>bar : Symbol(Dict_54886.bar, Decl(substitutionTypeUsedAsIndex.ts, 3, 9)) +} + +type FF_54886 = "foo" extends "foo" | "bar" ? "foo" : never; +>FF_54886 : Symbol(FF_54886, Decl(substitutionTypeUsedAsIndex.ts, 5, 1)) + +type C_54886 = Dict_54886[FF_54886]; // ok +>C_54886 : Symbol(C_54886, Decl(substitutionTypeUsedAsIndex.ts, 7, 60)) +>Dict_54886 : Symbol(Dict_54886, Decl(substitutionTypeUsedAsIndex.ts, 0, 0)) +>FF_54886 : Symbol(FF_54886, Decl(substitutionTypeUsedAsIndex.ts, 5, 1)) + diff --git a/tests/baselines/reference/substitutionTypeUsedAsIndex.types b/tests/baselines/reference/substitutionTypeUsedAsIndex.types new file mode 100644 index 0000000000000..b05d0e2a54e2a --- /dev/null +++ b/tests/baselines/reference/substitutionTypeUsedAsIndex.types @@ -0,0 +1,23 @@ +//// [tests/cases/compiler/substitutionTypeUsedAsIndex.ts] //// + +=== substitutionTypeUsedAsIndex.ts === +// repro from https://github.com/microsoft/TypeScript/issues/54886 + +interface Dict_54886 { + foo: 1; +>foo : 1 +> : ^ + + bar: 1; +>bar : 1 +> : ^ +} + +type FF_54886 = "foo" extends "foo" | "bar" ? "foo" : never; +>FF_54886 : "foo" +> : ^^^^^ + +type C_54886 = Dict_54886[FF_54886]; // ok +>C_54886 : 1 +> : ^ + diff --git a/tests/cases/compiler/substitutionTypeUsedAsIndex.ts b/tests/cases/compiler/substitutionTypeUsedAsIndex.ts new file mode 100644 index 0000000000000..3a880c039cc5d --- /dev/null +++ b/tests/cases/compiler/substitutionTypeUsedAsIndex.ts @@ -0,0 +1,12 @@ +// @strict: true +// @noEmit: true + +// repro from https://github.com/microsoft/TypeScript/issues/54886 + +interface Dict_54886 { + foo: 1; + bar: 1; +} + +type FF_54886 = "foo" extends "foo" | "bar" ? "foo" : never; +type C_54886 = Dict_54886[FF_54886]; // ok