From 16c1d9dc9e6b468055a8d1052ef270f1946828b0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 22 Nov 2023 14:01:35 -0800 Subject: [PATCH 1/3] Reduce intersections of constrained type variables and primitive types --- src/compiler/checker.ts | 84 +++++++++++++++++++++++++++++++++++++++-- src/compiler/types.ts | 4 ++ 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8ae0ea8980e21..03bdbf2a73423 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16794,6 +16794,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!(flags & TypeFlags.Never)) { includes |= flags & TypeFlags.IncludesMask; if (flags & TypeFlags.Instantiable) includes |= TypeFlags.IncludesInstantiable; + if (flags & TypeFlags.Intersection && getObjectFlags(type) & ObjectFlags.IsConstrainedTypeVariable) includes |= TypeFlags.IncludesConstrainedTypeVariable; if (type === wildcardType) includes |= TypeFlags.IncludesWildcard; if (!strictNullChecks && flags & TypeFlags.Nullable) { if (!(getObjectFlags(type) & ObjectFlags.ContainsWideningType)) includes |= TypeFlags.IncludesNonWideningType; @@ -16938,6 +16939,49 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function removeConstrainedTypeVariables(types: Type[]) { + const typeVariables: TypeVariable[] = []; + // First collect a list of the type variables occurring in constraining intersections. + for (const type of types) { + if (getObjectFlags(type) & ObjectFlags.IsConstrainedTypeVariable) { + const index = (type as IntersectionType).types[0].flags & TypeFlags.TypeVariable ? 0 : 1; + pushIfUnique(typeVariables, (type as IntersectionType).types[index]); + } + } + // For each type variable, check if the constraining intersections for that type variable fully + // cover the constraint of the type variable; if so, remove the constraining intersections and + // substitute the type variable. + for (const typeVariable of typeVariables) { + const primitives: Type[] = []; + // First collect the primitive types from the constraining intersections. + for (const type of types) { + if (getObjectFlags(type) & ObjectFlags.IsConstrainedTypeVariable) { + const index = (type as IntersectionType).types[0].flags & TypeFlags.TypeVariable ? 0 : 1; + if ((type as IntersectionType).types[index] === typeVariable) { + insertType(primitives, (type as IntersectionType).types[1 - index]); + } + } + } + // If every constituent in the type variable's constraint is covered by an intersection of the type + // variable and that constituent, remove those intersections and substitute the type variable. + const constraint = getBaseConstraintOfType(typeVariable)!; + if (everyType(constraint, t => containsType(primitives, t))) { + let i = types.length; + while (i > 0) { + i--; + const type = types[i]; + if (getObjectFlags(type) & ObjectFlags.IsConstrainedTypeVariable) { + const index = (type as IntersectionType).types[0].flags & TypeFlags.TypeVariable ? 0 : 1; + if ((type as IntersectionType).types[index] === typeVariable && containsType(primitives, (type as IntersectionType).types[1 - index])) { + orderedRemoveItemAt(types, i); + } + } + } + insertType(types, typeVariable); + } + } + } + function isNamedUnionType(type: Type) { return !!(type.flags & TypeFlags.Union && (type.aliasSymbol || (type as UnionType).origin)); } @@ -17012,6 +17056,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (includes & TypeFlags.StringLiteral && includes & TypeFlags.TemplateLiteral) { removeStringLiteralsMatchedByTemplateLiterals(typeSet); } + if (includes & TypeFlags.IncludesConstrainedTypeVariable) { + removeConstrainedTypeVariables(typeSet); + } if (unionReduction === UnionReduction.Subtype) { typeSet = removeSubtypes(typeSet, !!(includes & TypeFlags.Object)); if (!typeSet) { @@ -17276,9 +17323,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return true; } - function createIntersectionType(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]) { + function createIntersectionType(types: Type[], objectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]) { const result = createType(TypeFlags.Intersection) as IntersectionType; - result.objectFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); + result.objectFlags = objectFlags | getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); result.types = types; result.aliasSymbol = aliasSymbol; result.aliasTypeArguments = aliasTypeArguments; @@ -17299,6 +17346,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const typeMembershipMap = new Map(); const includes = addTypesToIntersection(typeMembershipMap, 0 as TypeFlags, types); const typeSet: Type[] = arrayFrom(typeMembershipMap.values()); + let objectFlags = ObjectFlags.None; // An intersection type is considered empty if it contains // the type never, or // more than one unit type or, @@ -17350,6 +17398,36 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (typeSet.length === 1) { return typeSet[0]; } + if (typeSet.length === 2) { + const typeVarIndex = typeSet[0].flags & TypeFlags.TypeVariable ? 0 : 1; + const typeVariable = typeSet[typeVarIndex]; + const primitiveType = typeSet[1 - typeVarIndex]; + if (typeVariable.flags & TypeFlags.TypeVariable && (primitiveType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive) || includes & TypeFlags.IncludesEmptyObject)) { + // We have an intersection T & P or P & T, where T is a type variable and P is a primitive type, the object type, or {}. + const constraint = getBaseConstraintOfType(typeVariable); + // Check that T's constraint is similarly composed of primitive types, the object type, or {}. + if (constraint && everyType(constraint, t => !!(t.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) || isEmptyAnonymousObjectType(t))) { + // If T's constraint is a subtype of P, simply return T. For example, given `T extends "a" | "b"`, + // the intersection `T & string` reduces to just T. + if (isTypeStrictSubtypeOf(constraint, primitiveType)) { + return typeVariable; + } + if (!(constraint.flags & TypeFlags.Union && someType(constraint, c => isTypeStrictSubtypeOf(c, primitiveType)))) { + // No constituent of T's constraint is a subtype of P. If P is also not a subtype of T's constraint, + // then the constraint and P are unrelated, and the intersection reduces to never. For example, given + // `T extends "a" | "b"`, the intersection `T & number` reduces to never. + if (!isTypeStrictSubtypeOf(primitiveType, constraint)) { + return neverType; + } + } + // Some constituent of T's constraint is a subtype of P, or P is a subtype of T's constraint. Thus, + // the intersection further constrains the type variable. For example, given `T extends string | number`, + // the intersection `T & "a"` is marked as a constrained type variable. Likewise, given `T extends "a" | 1`, + // the intersection `T & number` is marked as a constrained type variable. + objectFlags = ObjectFlags.IsConstrainedTypeVariable; + } + } + } const id = getTypeListId(typeSet) + getAliasId(aliasSymbol, aliasTypeArguments); let result = intersectionTypes.get(id); if (!result) { @@ -17385,7 +17463,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } else { - result = createIntersectionType(typeSet, aliasSymbol, aliasTypeArguments); + result = createIntersectionType(typeSet, objectFlags, aliasSymbol, aliasTypeArguments); } intersectionTypes.set(id, result); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9b0d53efce953..b7e88a65d64dd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6153,6 +6153,8 @@ export const enum TypeFlags { /** @internal */ IncludesInstantiable = Substitution, /** @internal */ + IncludesConstrainedTypeVariable = StringMapping, + /** @internal */ NotPrimitiveUnion = Any | Unknown | Void | Never | Object | Intersection | IncludesInstantiable, } @@ -6313,6 +6315,8 @@ export const enum ObjectFlags { IsNeverIntersectionComputed = 1 << 24, // IsNeverLike flag has been computed /** @internal */ IsNeverIntersection = 1 << 25, // Intersection reduces to never + /** @internal */ + IsConstrainedTypeVariable = 1 << 26, // T & C, where T's constraint and C are primitives, object, or {} } /** @internal */ From 4bf6ae78b8aa9184b7b886c5665899566b363b47 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 22 Nov 2023 14:02:12 -0800 Subject: [PATCH 2/3] Accept new baselines --- .../reference/conditionalTypes1.errors.txt | 1 + .../reference/inKeywordAndUnknown.types | 10 +++---- .../inKeywordTypeguard(strict=false).types | 4 +-- .../inKeywordTypeguard(strict=true).types | 4 +-- .../reference/indexSignatures1.errors.txt | 8 +++++- .../reference/indexSignatures1.types | 2 +- .../intersectionWithUnionConstraint.types | 4 +-- .../reference/spreadObjectOrFalsy.js | 2 +- .../reference/spreadObjectOrFalsy.types | 14 +++++----- .../reference/unknownControlFlow.errors.txt | 8 +++++- .../reference/unknownControlFlow.types | 28 +++++++++---------- 11 files changed, 49 insertions(+), 36 deletions(-) diff --git a/tests/baselines/reference/conditionalTypes1.errors.txt b/tests/baselines/reference/conditionalTypes1.errors.txt index c3b5f377180ff..ea66f14d92783 100644 --- a/tests/baselines/reference/conditionalTypes1.errors.txt +++ b/tests/baselines/reference/conditionalTypes1.errors.txt @@ -88,6 +88,7 @@ conditionalTypes1.ts(288,43): error TS2322: Type 'T95' is not assignable to t !!! error TS2322: Type 'T' is not assignable to type 'NonNullable'. !!! error TS2322: Type 'T' is not assignable to type '{}'. !!! related TS2208 conditionalTypes1.ts:10:13: This type parameter might need an `extends {}` constraint. +!!! related TS2208 conditionalTypes1.ts:10:13: This type parameter might need an `extends NonNullable` constraint. } function f2(x: T, y: NonNullable) { diff --git a/tests/baselines/reference/inKeywordAndUnknown.types b/tests/baselines/reference/inKeywordAndUnknown.types index a45017bd52d28..81a24b272970f 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.types +++ b/tests/baselines/reference/inKeywordAndUnknown.types @@ -138,12 +138,12 @@ function f5(x: T & {}) { function f6(x: T & {}) { >f6 : (x: T & {}) => boolean ->x : T & {} +>x : T return x instanceof Object && 'a' in x; >x instanceof Object && 'a' in x : boolean >x instanceof Object : boolean ->x : T & {} +>x : T >Object : ObjectConstructor >'a' in x : boolean >'a' : "a" @@ -152,15 +152,15 @@ function f6(x: T & {}) { function f7(x: T & {}) { >f7 : (x: T & {}) => boolean ->x : T & {} +>x : T return x instanceof Object && 'a' in x; >x instanceof Object && 'a' in x : boolean >x instanceof Object : boolean ->x : T & {} +>x : T >Object : ObjectConstructor >'a' in x : boolean >'a' : "a" ->x : T & {} +>x : T } diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).types b/tests/baselines/reference/inKeywordTypeguard(strict=false).types index 66ce2de68eee0..2caf941dedaac 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=false).types +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).types @@ -1078,12 +1078,12 @@ function isHTMLTable(table: T): boolean { const f =

(a: P & {}) => { >f :

(a: P & {}) => void >

(a: P & {}) => { "foo" in a;} :

(a: P & {}) => void ->a : P & {} +>a : P "foo" in a; >"foo" in a : boolean >"foo" : "foo" ->a : P & {} +>a : P }; diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).types b/tests/baselines/reference/inKeywordTypeguard(strict=true).types index 4a2a2094181b7..004ea8e7e2038 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=true).types +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).types @@ -1078,12 +1078,12 @@ function isHTMLTable(table: T): boolean { const f =

(a: P & {}) => { >f :

(a: P & {}) => void >

(a: P & {}) => { "foo" in a;} :

(a: P & {}) => void ->a : P & {} +>a : P "foo" in a; >"foo" in a : boolean >"foo" : "foo" ->a : P & {} +>a : P }; diff --git a/tests/baselines/reference/indexSignatures1.errors.txt b/tests/baselines/reference/indexSignatures1.errors.txt index ae9b07b65cec9..05d0436ca43c1 100644 --- a/tests/baselines/reference/indexSignatures1.errors.txt +++ b/tests/baselines/reference/indexSignatures1.errors.txt @@ -15,8 +15,10 @@ indexSignatures1.ts(73,5): error TS2374: Duplicate index signature for type '`fo indexSignatures1.ts(81,5): error TS2413: '`a${string}a`' index type '"c"' is not assignable to '`${string}a`' index type '"b"'. indexSignatures1.ts(81,5): error TS2413: '`a${string}a`' index type '"c"' is not assignable to '`a${string}`' index type '"a"'. indexSignatures1.ts(87,6): error TS1337: An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead. +indexSignatures1.ts(88,5): error TS2374: Duplicate index signature for type 'T'. indexSignatures1.ts(88,6): error TS1337: An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead. indexSignatures1.ts(89,6): error TS1268: An index signature parameter type must be 'string', 'number', 'symbol', or a template literal type. +indexSignatures1.ts(90,5): error TS2374: Duplicate index signature for type 'T'. indexSignatures1.ts(90,6): error TS1337: An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead. indexSignatures1.ts(117,1): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'I1'. No index signature with a parameter of type 'string' was found on type 'I1'. @@ -69,7 +71,7 @@ indexSignatures1.ts(289,7): error TS2322: Type 'number' is not assignable to typ indexSignatures1.ts(312,43): error TS2353: Object literal may only specify known properties, and '[sym]' does not exist in type '{ [key: number]: string; }'. -==== indexSignatures1.ts (50 errors) ==== +==== indexSignatures1.ts (52 errors) ==== // Symbol index signature checking const sym = Symbol(); @@ -188,12 +190,16 @@ indexSignatures1.ts(312,43): error TS2353: Object literal may only specify known ~~~ !!! error TS1337: An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead. [key: T | number]: string; // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2374: Duplicate index signature for type 'T'. ~~~ !!! error TS1337: An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead. [key: Error]: string; // Error ~~~ !!! error TS1268: An index signature parameter type must be 'string', 'number', 'symbol', or a template literal type. [key: T & string]: string; // Error + ~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2374: Duplicate index signature for type 'T'. ~~~ !!! error TS1337: An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead. } diff --git a/tests/baselines/reference/indexSignatures1.types b/tests/baselines/reference/indexSignatures1.types index 4f7e248192fb8..144face6af93b 100644 --- a/tests/baselines/reference/indexSignatures1.types +++ b/tests/baselines/reference/indexSignatures1.types @@ -261,7 +261,7 @@ type Invalid = { >key : Error [key: T & string]: string; // Error ->key : T & string +>key : T } // Intersections in index signatures diff --git a/tests/baselines/reference/intersectionWithUnionConstraint.types b/tests/baselines/reference/intersectionWithUnionConstraint.types index d687036196c4a..86c318fe89bb1 100644 --- a/tests/baselines/reference/intersectionWithUnionConstraint.types +++ b/tests/baselines/reference/intersectionWithUnionConstraint.types @@ -45,7 +45,7 @@ type T1 = (string | number | undefined) & (string | null | undefined); // strin function f3(x: T & (number | object | undefined)) { >f3 : (x: T & (number | object | undefined)) => void ->x : T & (number | object | undefined) +>x : (T & undefined) | (T & number) const y: number | undefined = x; >y : number | undefined @@ -54,7 +54,7 @@ function f3(x: T & (number | object | und function f4(x: T & (number | object)) { >f4 : (x: T & (number | object)) => void ->x : T & (number | object) +>x : T & number const y: number = x; >y : number diff --git a/tests/baselines/reference/spreadObjectOrFalsy.js b/tests/baselines/reference/spreadObjectOrFalsy.js index c5ee59ce93a9b..979b98f14d411 100644 --- a/tests/baselines/reference/spreadObjectOrFalsy.js +++ b/tests/baselines/reference/spreadObjectOrFalsy.js @@ -113,7 +113,7 @@ declare function f5(a: S | T): S | T; declare function f6(a: T): T; declare function g1(a: A): T | (undefined & T); +}>(a: A): T; interface DatafulFoo { data: T; } diff --git a/tests/baselines/reference/spreadObjectOrFalsy.types b/tests/baselines/reference/spreadObjectOrFalsy.types index b984a20079a27..3f57c796bdf7b 100644 --- a/tests/baselines/reference/spreadObjectOrFalsy.types +++ b/tests/baselines/reference/spreadObjectOrFalsy.types @@ -58,19 +58,19 @@ function f6(a: T) { // Repro from #46976 function g1(a: A) { ->g1 : (a: A) => T | (undefined & T) ->z : T | (undefined & T) +>g1 : (a: A) => T +>z : T >a : A const { z } = a; ->z : T | (undefined & T) +>z : T >a : A return { ->{ ...z } : T | (undefined & T) +>{ ...z } : T ...z ->z : T | (undefined & T) +>z : T }; } @@ -100,9 +100,9 @@ class Foo { this.data.toLocaleLowerCase(); >this.data.toLocaleLowerCase() : string >this.data.toLocaleLowerCase : (locales?: string | string[] | undefined) => string ->this.data : T | (undefined & T) +>this.data : T >this : this & DatafulFoo ->data : T | (undefined & T) +>data : T >toLocaleLowerCase : (locales?: string | string[] | undefined) => string } } diff --git a/tests/baselines/reference/unknownControlFlow.errors.txt b/tests/baselines/reference/unknownControlFlow.errors.txt index 2fa0730617da2..e8f4f198a505a 100644 --- a/tests/baselines/reference/unknownControlFlow.errors.txt +++ b/tests/baselines/reference/unknownControlFlow.errors.txt @@ -3,9 +3,11 @@ unknownControlFlow.ts(283,5): error TS2536: Type 'keyof (T & {})' cannot be used unknownControlFlow.ts(290,11): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'. unknownControlFlow.ts(291,5): error TS2345: Argument of type 'null' is not assignable to parameter of type 'never'. unknownControlFlow.ts(293,5): error TS2345: Argument of type 'null' is not assignable to parameter of type 'never'. +unknownControlFlow.ts(323,9): error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. +unknownControlFlow.ts(341,9): error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. -==== unknownControlFlow.ts (5 errors) ==== +==== unknownControlFlow.ts (7 errors) ==== type T01 = {} & string; // {} & string type T02 = {} & 'a'; // 'a' type T03 = {} & object; // object @@ -339,6 +341,8 @@ unknownControlFlow.ts(293,5): error TS2345: Argument of type 'null' is not assig function fx2(value: T & ({} | null)) { if (value === 42) { + ~~~~~~~~~~~~ +!!! error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. value; // T & {} } else { @@ -357,6 +361,8 @@ unknownControlFlow.ts(293,5): error TS2345: Argument of type 'null' is not assig function fx4(value: T & ({} | null)) { if (value === 42) { + ~~~~~~~~~~~~ +!!! error TS2367: This comparison appears to be unintentional because the types 'T' and 'number' have no overlap. value; // T & {} } else { diff --git a/tests/baselines/reference/unknownControlFlow.types b/tests/baselines/reference/unknownControlFlow.types index 4f452904c7105..7b320964b838a 100644 --- a/tests/baselines/reference/unknownControlFlow.types +++ b/tests/baselines/reference/unknownControlFlow.types @@ -819,55 +819,55 @@ function fx1(value: T & ({} | null)) { function fx2(value: T & ({} | null)) { >fx2 : (value: T & ({} | null)) => void ->value : T & ({} | null) +>value : T if (value === 42) { >value === 42 : boolean ->value : T & ({} | null) +>value : T >42 : 42 value; // T & {} ->value : T & ({} | null) +>value : never } else { value; // T & ({} | null) ->value : T & ({} | null) +>value : T } } function fx3(value: T & ({} | null)) { >fx3 : (value: T & ({} | null)) => void ->value : T & ({} | null) +>value : T & {} if (value === 42) { >value === 42 : boolean ->value : T & ({} | null) +>value : T & {} >42 : 42 value; // T & {} ->value : T & ({} | null) +>value : T & {} } else { value; // T & ({} | null) ->value : T & ({} | null) +>value : T & {} } } function fx4(value: T & ({} | null)) { >fx4 : (value: T & ({} | null)) => void ->value : T & ({} | null) +>value : T if (value === 42) { >value === 42 : boolean ->value : T & ({} | null) +>value : T >42 : 42 value; // T & {} ->value : T & {} +>value : never } else { value; // T & ({} | null) ->value : T & ({} | null) +>value : T } } @@ -1036,12 +1036,12 @@ type AB = "A" | "B"; function x(x: T_AB & undefined, y: any) { >x : (x: T_AB & undefined, y: any) => void ->x : T_AB & undefined +>x : never >y : any let r2: never = y as T_AB & undefined; >r2 : never ->y as T_AB & undefined : T_AB & undefined +>y as T_AB & undefined : never >y : any } From 5b6f969cd8ef0b794449c5bf47c726ea0705faee Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 22 Nov 2023 14:14:10 -0800 Subject: [PATCH 3/3] Add tests --- ...ypeVariableConstraintIntersections.symbols | 335 ++++++++++++++++++ .../typeVariableConstraintIntersections.types | 258 ++++++++++++++ .../typeVariableConstraintIntersections.ts | 95 +++++ 3 files changed, 688 insertions(+) create mode 100644 tests/baselines/reference/typeVariableConstraintIntersections.symbols create mode 100644 tests/baselines/reference/typeVariableConstraintIntersections.types create mode 100644 tests/cases/compiler/typeVariableConstraintIntersections.ts diff --git a/tests/baselines/reference/typeVariableConstraintIntersections.symbols b/tests/baselines/reference/typeVariableConstraintIntersections.symbols new file mode 100644 index 0000000000000..ab22dba71173d --- /dev/null +++ b/tests/baselines/reference/typeVariableConstraintIntersections.symbols @@ -0,0 +1,335 @@ +//// [tests/cases/compiler/typeVariableConstraintIntersections.ts] //// + +=== typeVariableConstraintIntersections.ts === +type T00 = K & "a"; +>T00 : Symbol(T00, Decl(typeVariableConstraintIntersections.ts, 0, 0)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 0, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 0, 9)) + +type T01 = K & "c"; +>T01 : Symbol(T01, Decl(typeVariableConstraintIntersections.ts, 0, 40)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 1, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 1, 9)) + +type T02 = K & string; +>T02 : Symbol(T02, Decl(typeVariableConstraintIntersections.ts, 1, 40)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 2, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 2, 9)) + +type T10 = K & "a"; +>T10 : Symbol(T10, Decl(typeVariableConstraintIntersections.ts, 2, 43)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 4, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 4, 9)) + +type T11 = K & "c"; +>T11 : Symbol(T11, Decl(typeVariableConstraintIntersections.ts, 4, 37)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 5, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 5, 9)) + +type T12 = K & string; +>T12 : Symbol(T12, Decl(typeVariableConstraintIntersections.ts, 5, 37)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 6, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 6, 9)) + +type T20 = K & ("a" | "b" | "c"); +>T20 : Symbol(T20, Decl(typeVariableConstraintIntersections.ts, 6, 40)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 8, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 8, 9)) + +type T21 = ("a" | "b" | "c") & K; +>T21 : Symbol(T21, Decl(typeVariableConstraintIntersections.ts, 8, 60)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 9, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 9, 9)) + +type T22 = K & ("a" | "b"); +>T22 : Symbol(T22, Decl(typeVariableConstraintIntersections.ts, 9, 60)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 10, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 10, 9)) + +type T23 = ("a" | "b") & K; +>T23 : Symbol(T23, Decl(typeVariableConstraintIntersections.ts, 10, 54)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 11, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 11, 9)) + +type T30 = K & ("a" | "b" | "c"); +>T30 : Symbol(T30, Decl(typeVariableConstraintIntersections.ts, 11, 54)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 13, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 13, 9)) + +type T31 = ("a" | "b" | "c") & K; +>T31 : Symbol(T31, Decl(typeVariableConstraintIntersections.ts, 13, 54)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 14, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 14, 9)) + +type T32 = K & ("a" | "b"); +>T32 : Symbol(T32, Decl(typeVariableConstraintIntersections.ts, 14, 54)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 15, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 15, 9)) + +type T33 = ("a" | "b") & K; +>T33 : Symbol(T33, Decl(typeVariableConstraintIntersections.ts, 15, 48)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 16, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 16, 9)) + +type T40 = K & undefined; +>T40 : Symbol(T40, Decl(typeVariableConstraintIntersections.ts, 16, 48)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 18, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 18, 9)) + +type T41 = K & null; +>T41 : Symbol(T41, Decl(typeVariableConstraintIntersections.ts, 18, 39)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 19, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 19, 9)) + +type T42 = K & object; +>T42 : Symbol(T42, Decl(typeVariableConstraintIntersections.ts, 19, 34)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 20, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 20, 9)) + +type T43 = K & {}; +>T43 : Symbol(T43, Decl(typeVariableConstraintIntersections.ts, 20, 36)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 21, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 21, 9)) + +type T50 = K & "a"; +>T50 : Symbol(T50, Decl(typeVariableConstraintIntersections.ts, 21, 32)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 23, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 23, 9)) + +type T51 = K & "b"; +>T51 : Symbol(T51, Decl(typeVariableConstraintIntersections.ts, 23, 38)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 24, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 24, 9)) + +type T52 = K & string; +>T52 : Symbol(T52, Decl(typeVariableConstraintIntersections.ts, 24, 38)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 25, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 25, 9)) + +type T53 = K & 0; +>T53 : Symbol(T53, Decl(typeVariableConstraintIntersections.ts, 25, 41)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 26, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 26, 9)) + +type T54 = K & 1; +>T54 : Symbol(T54, Decl(typeVariableConstraintIntersections.ts, 26, 36)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 27, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 27, 9)) + +type T55 = K & number; +>T55 : Symbol(T55, Decl(typeVariableConstraintIntersections.ts, 27, 36)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 28, 9)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 28, 9)) + +type T60 = U & "a"; +>T60 : Symbol(T60, Decl(typeVariableConstraintIntersections.ts, 28, 41)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 30, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 30, 29)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 30, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 30, 29)) + +type T61 = U & ("a" | "b"); +>T61 : Symbol(T61, Decl(typeVariableConstraintIntersections.ts, 30, 53)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 31, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 31, 29)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 31, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 31, 29)) + +type T62 = U & ("a" | "b" | "c"); +>T62 : Symbol(T62, Decl(typeVariableConstraintIntersections.ts, 31, 61)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 32, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 32, 29)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 32, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 32, 29)) + +type T63 = U & string; +>T63 : Symbol(T63, Decl(typeVariableConstraintIntersections.ts, 32, 67)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 33, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 33, 29)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 33, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 33, 29)) + +type T70 = U & "a"; +>T70 : Symbol(T70, Decl(typeVariableConstraintIntersections.ts, 33, 56)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 35, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 35, 29)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 35, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 35, 29)) + +type T71 = U & ("a" | "b"); +>T71 : Symbol(T71, Decl(typeVariableConstraintIntersections.ts, 35, 59)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 36, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 36, 29)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 36, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 36, 29)) + +type T72 = U & ("a" | "b" | "c"); +>T72 : Symbol(T72, Decl(typeVariableConstraintIntersections.ts, 36, 67)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 37, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 37, 29)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 37, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 37, 29)) + +type T73 = U & string; +>T73 : Symbol(T73, Decl(typeVariableConstraintIntersections.ts, 37, 73)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 38, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 38, 29)) +>T : Symbol(T, Decl(typeVariableConstraintIntersections.ts, 38, 9)) +>U : Symbol(U, Decl(typeVariableConstraintIntersections.ts, 38, 29)) + +declare function isA(x: any): x is "a"; +>isA : Symbol(isA, Decl(typeVariableConstraintIntersections.ts, 38, 62)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 40, 21)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 40, 21)) + +declare function isB(x: any): x is "b"; +>isB : Symbol(isB, Decl(typeVariableConstraintIntersections.ts, 40, 39)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 41, 21)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 41, 21)) + +declare function isC(x: any): x is "c"; +>isC : Symbol(isC, Decl(typeVariableConstraintIntersections.ts, 41, 39)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 42, 21)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 42, 21)) + +function foo(x: K) { +>foo : Symbol(foo, Decl(typeVariableConstraintIntersections.ts, 42, 39)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 44, 13)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 44, 13)) + + if (isA(x)) { +>isA : Symbol(isA, Decl(typeVariableConstraintIntersections.ts, 38, 62)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) + + x; // K & "a" +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) + } + if (isB(x)) { +>isB : Symbol(isB, Decl(typeVariableConstraintIntersections.ts, 40, 39)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) + + x; // K & "b" +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) + } + if (isC(x)) { +>isC : Symbol(isC, Decl(typeVariableConstraintIntersections.ts, 41, 39)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) + + x; // never +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) + } + if (isA(x) || isB(x)) { +>isA : Symbol(isA, Decl(typeVariableConstraintIntersections.ts, 38, 62)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) +>isB : Symbol(isB, Decl(typeVariableConstraintIntersections.ts, 40, 39)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) + + x; // K +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) + } + if (!(isA(x) || isB(x))) { +>isA : Symbol(isA, Decl(typeVariableConstraintIntersections.ts, 38, 62)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) +>isB : Symbol(isB, Decl(typeVariableConstraintIntersections.ts, 40, 39)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) + + return; + } + x; // K +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 44, 34)) +} + +// Example from #30581 + +type OptionOne = { +>OptionOne : Symbol(OptionOne, Decl(typeVariableConstraintIntersections.ts, 61, 1)) + + kind: "one"; +>kind : Symbol(kind, Decl(typeVariableConstraintIntersections.ts, 65, 18)) + + s: string; +>s : Symbol(s, Decl(typeVariableConstraintIntersections.ts, 66, 14)) + +}; + +type OptionTwo = { +>OptionTwo : Symbol(OptionTwo, Decl(typeVariableConstraintIntersections.ts, 68, 2)) + + kind: "two"; +>kind : Symbol(kind, Decl(typeVariableConstraintIntersections.ts, 70, 18)) + + x: number; +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 71, 14)) + + y: number; +>y : Symbol(y, Decl(typeVariableConstraintIntersections.ts, 72, 12)) + +}; + +type Options = OptionOne | OptionTwo; +>Options : Symbol(Options, Decl(typeVariableConstraintIntersections.ts, 74, 2)) +>OptionOne : Symbol(OptionOne, Decl(typeVariableConstraintIntersections.ts, 61, 1)) +>OptionTwo : Symbol(OptionTwo, Decl(typeVariableConstraintIntersections.ts, 68, 2)) + +type OptionHandlers = { +>OptionHandlers : Symbol(OptionHandlers, Decl(typeVariableConstraintIntersections.ts, 76, 37)) + + [K in Options['kind']]: (option: Options & { kind: K }) => string; +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 79, 3)) +>Options : Symbol(Options, Decl(typeVariableConstraintIntersections.ts, 74, 2)) +>option : Symbol(option, Decl(typeVariableConstraintIntersections.ts, 79, 27)) +>Options : Symbol(Options, Decl(typeVariableConstraintIntersections.ts, 74, 2)) +>kind : Symbol(kind, Decl(typeVariableConstraintIntersections.ts, 79, 46)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 79, 3)) +} + +const optionHandlers: OptionHandlers = { +>optionHandlers : Symbol(optionHandlers, Decl(typeVariableConstraintIntersections.ts, 82, 5)) +>OptionHandlers : Symbol(OptionHandlers, Decl(typeVariableConstraintIntersections.ts, 76, 37)) + + "one": option => option.s, +>"one" : Symbol("one", Decl(typeVariableConstraintIntersections.ts, 82, 40)) +>option : Symbol(option, Decl(typeVariableConstraintIntersections.ts, 83, 8)) +>option.s : Symbol(s, Decl(typeVariableConstraintIntersections.ts, 66, 14)) +>option : Symbol(option, Decl(typeVariableConstraintIntersections.ts, 83, 8)) +>s : Symbol(s, Decl(typeVariableConstraintIntersections.ts, 66, 14)) + + "two": option => option.x + "," + option.y, +>"two" : Symbol("two", Decl(typeVariableConstraintIntersections.ts, 83, 28)) +>option : Symbol(option, Decl(typeVariableConstraintIntersections.ts, 84, 8)) +>option.x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 71, 14)) +>option : Symbol(option, Decl(typeVariableConstraintIntersections.ts, 84, 8)) +>x : Symbol(x, Decl(typeVariableConstraintIntersections.ts, 71, 14)) +>option.y : Symbol(y, Decl(typeVariableConstraintIntersections.ts, 72, 12)) +>option : Symbol(option, Decl(typeVariableConstraintIntersections.ts, 84, 8)) +>y : Symbol(y, Decl(typeVariableConstraintIntersections.ts, 72, 12)) + +}; + +function handleOption(option: Options & { kind: K }): string { +>handleOption : Symbol(handleOption, Decl(typeVariableConstraintIntersections.ts, 85, 2)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 87, 22)) +>Options : Symbol(Options, Decl(typeVariableConstraintIntersections.ts, 74, 2)) +>option : Symbol(option, Decl(typeVariableConstraintIntersections.ts, 87, 49)) +>Options : Symbol(Options, Decl(typeVariableConstraintIntersections.ts, 74, 2)) +>kind : Symbol(kind, Decl(typeVariableConstraintIntersections.ts, 87, 68)) +>K : Symbol(K, Decl(typeVariableConstraintIntersections.ts, 87, 22)) + + const kind = option.kind; +>kind : Symbol(kind, Decl(typeVariableConstraintIntersections.ts, 88, 7)) +>option.kind : Symbol(kind, Decl(typeVariableConstraintIntersections.ts, 65, 18), Decl(typeVariableConstraintIntersections.ts, 87, 68), Decl(typeVariableConstraintIntersections.ts, 70, 18), Decl(typeVariableConstraintIntersections.ts, 87, 68)) +>option : Symbol(option, Decl(typeVariableConstraintIntersections.ts, 87, 49)) +>kind : Symbol(kind, Decl(typeVariableConstraintIntersections.ts, 65, 18), Decl(typeVariableConstraintIntersections.ts, 87, 68), Decl(typeVariableConstraintIntersections.ts, 70, 18), Decl(typeVariableConstraintIntersections.ts, 87, 68)) + + const handler = optionHandlers[kind]; +>handler : Symbol(handler, Decl(typeVariableConstraintIntersections.ts, 89, 7)) +>optionHandlers : Symbol(optionHandlers, Decl(typeVariableConstraintIntersections.ts, 82, 5)) +>kind : Symbol(kind, Decl(typeVariableConstraintIntersections.ts, 88, 7)) + + return handler(option); +>handler : Symbol(handler, Decl(typeVariableConstraintIntersections.ts, 89, 7)) +>option : Symbol(option, Decl(typeVariableConstraintIntersections.ts, 87, 49)) + +}; + diff --git a/tests/baselines/reference/typeVariableConstraintIntersections.types b/tests/baselines/reference/typeVariableConstraintIntersections.types new file mode 100644 index 0000000000000..6d89a64f8a0b8 --- /dev/null +++ b/tests/baselines/reference/typeVariableConstraintIntersections.types @@ -0,0 +1,258 @@ +//// [tests/cases/compiler/typeVariableConstraintIntersections.ts] //// + +=== typeVariableConstraintIntersections.ts === +type T00 = K & "a"; +>T00 : T00 + +type T01 = K & "c"; +>T01 : never + +type T02 = K & string; +>T02 : K + +type T10 = K & "a"; +>T10 : T10 + +type T11 = K & "c"; +>T11 : T11 + +type T12 = K & string; +>T12 : K + +type T20 = K & ("a" | "b" | "c"); +>T20 : K + +type T21 = ("a" | "b" | "c") & K; +>T21 : K + +type T22 = K & ("a" | "b"); +>T22 : T22 + +type T23 = ("a" | "b") & K; +>T23 : T23 + +type T30 = K & ("a" | "b" | "c"); +>T30 : K + +type T31 = ("a" | "b" | "c") & K; +>T31 : K + +type T32 = K & ("a" | "b"); +>T32 : K + +type T33 = ("a" | "b") & K; +>T33 : K + +type T40 = K & undefined; +>T40 : never + +type T41 = K & null; +>T41 : never + +type T42 = K & object; +>T42 : T42 + +type T43 = K & {}; +>T43 : K + +type T50 = K & "a"; +>T50 : T50 + +type T51 = K & "b"; +>T51 : never + +type T52 = K & string; +>T52 : T52 + +type T53 = K & 0; +>T53 : T53 + +type T54 = K & 1; +>T54 : never + +type T55 = K & number; +>T55 : T55 + +type T60 = U & "a"; +>T60 : T60 + +type T61 = U & ("a" | "b"); +>T61 : U + +type T62 = U & ("a" | "b" | "c"); +>T62 : U + +type T63 = U & string; +>T63 : U + +type T70 = U & "a"; +>T70 : T70 + +type T71 = U & ("a" | "b"); +>T71 : T71 + +type T72 = U & ("a" | "b" | "c"); +>T72 : U + +type T73 = U & string; +>T73 : U + +declare function isA(x: any): x is "a"; +>isA : (x: any) => x is "a" +>x : any + +declare function isB(x: any): x is "b"; +>isB : (x: any) => x is "b" +>x : any + +declare function isC(x: any): x is "c"; +>isC : (x: any) => x is "c" +>x : any + +function foo(x: K) { +>foo : (x: K) => void +>x : K + + if (isA(x)) { +>isA(x) : boolean +>isA : (x: any) => x is "a" +>x : "a" | "b" + + x; // K & "a" +>x : K & "a" + } + if (isB(x)) { +>isB(x) : boolean +>isB : (x: any) => x is "b" +>x : "a" | "b" + + x; // K & "b" +>x : K & "b" + } + if (isC(x)) { +>isC(x) : boolean +>isC : (x: any) => x is "c" +>x : "a" | "b" + + x; // never +>x : never + } + if (isA(x) || isB(x)) { +>isA(x) || isB(x) : boolean +>isA(x) : boolean +>isA : (x: any) => x is "a" +>x : "a" | "b" +>isB(x) : boolean +>isB : (x: any) => x is "b" +>x : "b" + + x; // K +>x : K + } + if (!(isA(x) || isB(x))) { +>!(isA(x) || isB(x)) : boolean +>(isA(x) || isB(x)) : boolean +>isA(x) || isB(x) : boolean +>isA(x) : boolean +>isA : (x: any) => x is "a" +>x : "a" | "b" +>isB(x) : boolean +>isB : (x: any) => x is "b" +>x : "b" + + return; + } + x; // K +>x : K +} + +// Example from #30581 + +type OptionOne = { +>OptionOne : { kind: "one"; s: string; } + + kind: "one"; +>kind : "one" + + s: string; +>s : string + +}; + +type OptionTwo = { +>OptionTwo : { kind: "two"; x: number; y: number; } + + kind: "two"; +>kind : "two" + + x: number; +>x : number + + y: number; +>y : number + +}; + +type Options = OptionOne | OptionTwo; +>Options : OptionOne | OptionTwo + +type OptionHandlers = { +>OptionHandlers : { one: (option: OptionOne & { kind: "one"; }) => string; two: (option: OptionTwo & { kind: "two"; }) => string; } + + [K in Options['kind']]: (option: Options & { kind: K }) => string; +>option : Options & { kind: K; } +>kind : K +} + +const optionHandlers: OptionHandlers = { +>optionHandlers : OptionHandlers +>{ "one": option => option.s, "two": option => option.x + "," + option.y,} : { one: (option: OptionOne & { kind: "one"; }) => string; two: (option: OptionTwo & { kind: "two"; }) => string; } + + "one": option => option.s, +>"one" : (option: OptionOne & { kind: "one"; }) => string +>option => option.s : (option: OptionOne & { kind: "one"; }) => string +>option : OptionOne & { kind: "one"; } +>option.s : string +>option : OptionOne & { kind: "one"; } +>s : string + + "two": option => option.x + "," + option.y, +>"two" : (option: OptionTwo & { kind: "two"; }) => string +>option => option.x + "," + option.y : (option: OptionTwo & { kind: "two"; }) => string +>option : OptionTwo & { kind: "two"; } +>option.x + "," + option.y : string +>option.x + "," : string +>option.x : number +>option : OptionTwo & { kind: "two"; } +>x : number +>"," : "," +>option.y : number +>option : OptionTwo & { kind: "two"; } +>y : number + +}; + +function handleOption(option: Options & { kind: K }): string { +>handleOption : (option: Options & { kind: K; }) => string +>option : Options & { kind: K; } +>kind : K + + const kind = option.kind; +>kind : K +>option.kind : K +>option : Options & { kind: K; } +>kind : K + + const handler = optionHandlers[kind]; +>handler : OptionHandlers[K] +>optionHandlers[kind] : OptionHandlers[K] +>optionHandlers : OptionHandlers +>kind : K + + return handler(option); +>handler(option) : string +>handler : OptionHandlers[K] +>option : Options & { kind: K; } + +}; + diff --git a/tests/cases/compiler/typeVariableConstraintIntersections.ts b/tests/cases/compiler/typeVariableConstraintIntersections.ts new file mode 100644 index 0000000000000..3d5d69fe3223d --- /dev/null +++ b/tests/cases/compiler/typeVariableConstraintIntersections.ts @@ -0,0 +1,95 @@ +// @strict: true +// @noEmit: true + +type T00 = K & "a"; +type T01 = K & "c"; +type T02 = K & string; + +type T10 = K & "a"; +type T11 = K & "c"; +type T12 = K & string; + +type T20 = K & ("a" | "b" | "c"); +type T21 = ("a" | "b" | "c") & K; +type T22 = K & ("a" | "b"); +type T23 = ("a" | "b") & K; + +type T30 = K & ("a" | "b" | "c"); +type T31 = ("a" | "b" | "c") & K; +type T32 = K & ("a" | "b"); +type T33 = ("a" | "b") & K; + +type T40 = K & undefined; +type T41 = K & null; +type T42 = K & object; +type T43 = K & {}; + +type T50 = K & "a"; +type T51 = K & "b"; +type T52 = K & string; +type T53 = K & 0; +type T54 = K & 1; +type T55 = K & number; + +type T60 = U & "a"; +type T61 = U & ("a" | "b"); +type T62 = U & ("a" | "b" | "c"); +type T63 = U & string; + +type T70 = U & "a"; +type T71 = U & ("a" | "b"); +type T72 = U & ("a" | "b" | "c"); +type T73 = U & string; + +declare function isA(x: any): x is "a"; +declare function isB(x: any): x is "b"; +declare function isC(x: any): x is "c"; + +function foo(x: K) { + if (isA(x)) { + x; // K & "a" + } + if (isB(x)) { + x; // K & "b" + } + if (isC(x)) { + x; // never + } + if (isA(x) || isB(x)) { + x; // K + } + if (!(isA(x) || isB(x))) { + return; + } + x; // K +} + +// Example from #30581 + +type OptionOne = { + kind: "one"; + s: string; +}; + +type OptionTwo = { + kind: "two"; + x: number; + y: number; +}; + +type Options = OptionOne | OptionTwo; + +type OptionHandlers = { + [K in Options['kind']]: (option: Options & { kind: K }) => string; +} + +const optionHandlers: OptionHandlers = { + "one": option => option.s, + "two": option => option.x + "," + option.y, +}; + +function handleOption(option: Options & { kind: K }): string { + const kind = option.kind; + const handler = optionHandlers[kind]; + return handler(option); +};