Reduce intersections of constrained type variables and primitive types (#56515)

This commit is contained in:
Anders Hejlsberg 2023-11-30 07:04:09 -08:00 committed by GitHub
parent f834133fe2
commit 3258d75169
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 835 additions and 52 deletions

View File

@ -16824,6 +16824,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;
@ -16968,6 +16969,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));
}
@ -17042,6 +17086,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) {
@ -17306,9 +17353,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;
@ -17329,6 +17376,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const typeMembershipMap = new Map<string, Type>();
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,
@ -17380,6 +17428,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) {
@ -17415,7 +17493,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
else {
result = createIntersectionType(typeSet, aliasSymbol, aliasTypeArguments);
result = createIntersectionType(typeSet, objectFlags, aliasSymbol, aliasTypeArguments);
}
intersectionTypes.set(id, result);
}

View File

@ -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 */

View File

@ -88,6 +88,7 @@ conditionalTypes1.ts(288,43): error TS2322: Type 'T95<U>' is not assignable to t
!!! error TS2322: Type 'T' is not assignable to type 'NonNullable<T>'.
!!! 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<T>` constraint.
}
function f2<T extends string | undefined>(x: T, y: NonNullable<T>) {

View File

@ -138,12 +138,12 @@ function f5<T>(x: T & {}) {
function f6<T extends {}>(x: T & {}) {
>f6 : <T extends {}>(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<T extends {}>(x: T & {}) {
function f7<T extends object>(x: T & {}) {
>f7 : <T extends object>(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
}

View File

@ -1078,12 +1078,12 @@ function isHTMLTable<T extends object | null>(table: T): boolean {
const f = <P extends object>(a: P & {}) => {
>f : <P extends object>(a: P & {}) => void
><P extends object>(a: P & {}) => { "foo" in a;} : <P extends object>(a: P & {}) => void
>a : P & {}
>a : P
"foo" in a;
>"foo" in a : boolean
>"foo" : "foo"
>a : P & {}
>a : P
};

View File

@ -1078,12 +1078,12 @@ function isHTMLTable<T extends object | null>(table: T): boolean {
const f = <P extends object>(a: P & {}) => {
>f : <P extends object>(a: P & {}) => void
><P extends object>(a: P & {}) => { "foo" in a;} : <P extends object>(a: P & {}) => void
>a : P & {}
>a : P
"foo" in a;
>"foo" in a : boolean
>"foo" : "foo"
>a : P & {}
>a : P
};

View File

@ -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.
}

View File

@ -261,7 +261,7 @@ type Invalid<T extends string> = {
>key : Error
[key: T & string]: string; // Error
>key : T & string
>key : T
}
// Intersections in index signatures

View File

@ -45,7 +45,7 @@ type T1 = (string | number | undefined) & (string | null | undefined); // strin
function f3<T extends string | number | undefined>(x: T & (number | object | undefined)) {
>f3 : <T extends string | number | undefined>(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<T extends string | number | undefined>(x: T & (number | object | und
function f4<T extends string | number>(x: T & (number | object)) {
>f4 : <T extends string | number>(x: T & (number | object)) => void
>x : T & (number | object)
>x : T & number
const y: number = x;
>y : number

View File

@ -113,7 +113,7 @@ declare function f5<S, T extends undefined>(a: S | T): S | T;
declare function f6<T extends object | undefined>(a: T): T;
declare function g1<T extends {}, A extends {
z: (T | undefined) & T;
}>(a: A): T | (undefined & T);
}>(a: A): T;
interface DatafulFoo<T> {
data: T;
}

View File

@ -58,19 +58,19 @@ function f6<T extends object | undefined>(a: T) {
// Repro from #46976
function g1<T extends {}, A extends { z: (T | undefined) & T }>(a: A) {
>g1 : <T extends {}, A extends { z: (T | undefined) & T; }>(a: A) => T | (undefined & T)
>z : T | (undefined & T)
>g1 : <T extends {}, A extends { z: (T | undefined) & T; }>(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<T extends string> {
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<T>
>data : T | (undefined & T)
>data : T
>toLocaleLowerCase : (locales?: string | string[] | undefined) => string
}
}

View File

@ -0,0 +1,335 @@
//// [tests/cases/compiler/typeVariableConstraintIntersections.ts] ////
=== typeVariableConstraintIntersections.ts ===
type T00<K extends "a" | "b"> = 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 extends "a" | "b"> = 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 extends "a" | "b"> = 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 extends string> = 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 extends string> = 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 extends string> = 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 extends "a" | "b" | "c"> = 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<K extends "a" | "b" | "c"> = ("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 extends "a" | "b" | "c"> = 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<K extends "a" | "b" | "c"> = ("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 extends "a" | "b"> = 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<K extends "a" | "b"> = ("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 extends "a" | "b"> = 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<K extends "a" | "b"> = ("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 extends {}> = 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 extends {}> = 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 extends {}> = 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 extends {}> = 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 extends "a" | 0> = 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 extends "a" | 0> = 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 extends "a" | 0> = 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 extends "a" | 0> = 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 extends "a" | 0> = 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 extends "a" | 0> = 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<T extends "a" | "b", U extends T> = 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<T extends "a" | "b", U extends T> = 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<T extends "a" | "b", U extends T> = 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<T extends "a" | "b", U extends T> = 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<T extends "a" | "b", U extends T | "c"> = 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<T extends "a" | "b", U extends T | "c"> = 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<T extends "a" | "b", U extends T | "c"> = 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<T extends "a" | "b", U extends T | "c"> = 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<K extends "a" | "b">(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<K extends Options['kind']>(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))
};

View File

@ -0,0 +1,258 @@
//// [tests/cases/compiler/typeVariableConstraintIntersections.ts] ////
=== typeVariableConstraintIntersections.ts ===
type T00<K extends "a" | "b"> = K & "a";
>T00 : T00<K>
type T01<K extends "a" | "b"> = K & "c";
>T01 : never
type T02<K extends "a" | "b"> = K & string;
>T02 : K
type T10<K extends string> = K & "a";
>T10 : T10<K>
type T11<K extends string> = K & "c";
>T11 : T11<K>
type T12<K extends string> = K & string;
>T12 : K
type T20<K extends "a" | "b" | "c"> = K & ("a" | "b" | "c");
>T20 : K
type T21<K extends "a" | "b" | "c"> = ("a" | "b" | "c") & K;
>T21 : K
type T22<K extends "a" | "b" | "c"> = K & ("a" | "b");
>T22 : T22<K>
type T23<K extends "a" | "b" | "c"> = ("a" | "b") & K;
>T23 : T23<K>
type T30<K extends "a" | "b"> = K & ("a" | "b" | "c");
>T30 : K
type T31<K extends "a" | "b"> = ("a" | "b" | "c") & K;
>T31 : K
type T32<K extends "a" | "b"> = K & ("a" | "b");
>T32 : K
type T33<K extends "a" | "b"> = ("a" | "b") & K;
>T33 : K
type T40<K extends {}> = K & undefined;
>T40 : never
type T41<K extends {}> = K & null;
>T41 : never
type T42<K extends {}> = K & object;
>T42 : T42<K>
type T43<K extends {}> = K & {};
>T43 : K
type T50<K extends "a" | 0> = K & "a";
>T50 : T50<K>
type T51<K extends "a" | 0> = K & "b";
>T51 : never
type T52<K extends "a" | 0> = K & string;
>T52 : T52<K>
type T53<K extends "a" | 0> = K & 0;
>T53 : T53<K>
type T54<K extends "a" | 0> = K & 1;
>T54 : never
type T55<K extends "a" | 0> = K & number;
>T55 : T55<K>
type T60<T extends "a" | "b", U extends T> = U & "a";
>T60 : T60<T, U>
type T61<T extends "a" | "b", U extends T> = U & ("a" | "b");
>T61 : U
type T62<T extends "a" | "b", U extends T> = U & ("a" | "b" | "c");
>T62 : U
type T63<T extends "a" | "b", U extends T> = U & string;
>T63 : U
type T70<T extends "a" | "b", U extends T | "c"> = U & "a";
>T70 : T70<T, U>
type T71<T extends "a" | "b", U extends T | "c"> = U & ("a" | "b");
>T71 : T71<T, U>
type T72<T extends "a" | "b", U extends T | "c"> = U & ("a" | "b" | "c");
>T72 : U
type T73<T extends "a" | "b", U extends T | "c"> = 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<K extends "a" | "b">(x: K) {
>foo : <K extends "a" | "b">(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<K extends Options['kind']>(option: Options & { kind: K }): string {
>handleOption : <K extends "one" | "two">(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; }
};

View File

@ -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<T extends {}>(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<T extends {} | null>(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 {

View File

@ -819,47 +819,29 @@ function fx1<T extends unknown>(value: T & ({} | null)) {
function fx2<T extends {}>(value: T & ({} | null)) {
>fx2 : <T extends {}>(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<T extends {} | undefined>(value: T & ({} | null)) {
>fx3 : <T extends {} | undefined>(value: T & ({} | null)) => void
>value : T & ({} | null)
>value : T & {}
if (value === 42) {
>value === 42 : boolean
>value : T & ({} | null)
>42 : 42
value; // T & {}
>value : T & ({} | null)
}
else {
value; // T & ({} | null)
>value : T & ({} | null)
}
}
function fx4<T extends {} | null>(value: T & ({} | null)) {
>fx4 : <T extends {} | null>(value: T & ({} | null)) => void
>value : T & ({} | null)
if (value === 42) {
>value === 42 : boolean
>value : T & ({} | null)
>value : T & {}
>42 : 42
value; // T & {}
@ -867,7 +849,25 @@ function fx4<T extends {} | null>(value: T & ({} | null)) {
}
else {
value; // T & ({} | null)
>value : T & ({} | null)
>value : T & {}
}
}
function fx4<T extends {} | null>(value: T & ({} | null)) {
>fx4 : <T extends {} | null>(value: T & ({} | null)) => void
>value : T
if (value === 42) {
>value === 42 : boolean
>value : T
>42 : 42
value; // T & {}
>value : never
}
else {
value; // T & ({} | null)
>value : T
}
}
@ -1036,12 +1036,12 @@ type AB = "A" | "B";
function x<T_AB extends AB>(x: T_AB & undefined, y: any) {
>x : <T_AB extends AB>(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
}

View File

@ -0,0 +1,95 @@
// @strict: true
// @noEmit: true
type T00<K extends "a" | "b"> = K & "a";
type T01<K extends "a" | "b"> = K & "c";
type T02<K extends "a" | "b"> = K & string;
type T10<K extends string> = K & "a";
type T11<K extends string> = K & "c";
type T12<K extends string> = K & string;
type T20<K extends "a" | "b" | "c"> = K & ("a" | "b" | "c");
type T21<K extends "a" | "b" | "c"> = ("a" | "b" | "c") & K;
type T22<K extends "a" | "b" | "c"> = K & ("a" | "b");
type T23<K extends "a" | "b" | "c"> = ("a" | "b") & K;
type T30<K extends "a" | "b"> = K & ("a" | "b" | "c");
type T31<K extends "a" | "b"> = ("a" | "b" | "c") & K;
type T32<K extends "a" | "b"> = K & ("a" | "b");
type T33<K extends "a" | "b"> = ("a" | "b") & K;
type T40<K extends {}> = K & undefined;
type T41<K extends {}> = K & null;
type T42<K extends {}> = K & object;
type T43<K extends {}> = K & {};
type T50<K extends "a" | 0> = K & "a";
type T51<K extends "a" | 0> = K & "b";
type T52<K extends "a" | 0> = K & string;
type T53<K extends "a" | 0> = K & 0;
type T54<K extends "a" | 0> = K & 1;
type T55<K extends "a" | 0> = K & number;
type T60<T extends "a" | "b", U extends T> = U & "a";
type T61<T extends "a" | "b", U extends T> = U & ("a" | "b");
type T62<T extends "a" | "b", U extends T> = U & ("a" | "b" | "c");
type T63<T extends "a" | "b", U extends T> = U & string;
type T70<T extends "a" | "b", U extends T | "c"> = U & "a";
type T71<T extends "a" | "b", U extends T | "c"> = U & ("a" | "b");
type T72<T extends "a" | "b", U extends T | "c"> = U & ("a" | "b" | "c");
type T73<T extends "a" | "b", U extends T | "c"> = 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<K extends "a" | "b">(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<K extends Options['kind']>(option: Options & { kind: K }): string {
const kind = option.kind;
const handler = optionHandlers[kind];
return handler(option);
};