mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
Fix equality narrowing and comparable relation for intersections with {} (#50735)
* Fox equality narrowing and comparable relation for intersections with {}
* Accept new baselines
* Add tests
* Accept new baselines
This commit is contained in:
parent
b23f1d6b59
commit
4110b80fbb
@ -19216,11 +19216,15 @@ namespace ts {
|
||||
// parameter 'T extends 1 | 2', the intersection 'T & 1' should be reduced to '1' such that it doesn't
|
||||
// appear to be comparable to '2'.
|
||||
if (relation === comparableRelation && target.flags & TypeFlags.Primitive) {
|
||||
const constraints = sameMap((source as IntersectionType).types, getBaseConstraintOrType);
|
||||
const constraints = sameMap((source as IntersectionType).types, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(t) || unknownType : t);
|
||||
if (constraints !== (source as IntersectionType).types) {
|
||||
source = getIntersectionType(constraints);
|
||||
if (source.flags & TypeFlags.Never) {
|
||||
return Ternary.False;
|
||||
}
|
||||
if (!(source.flags & TypeFlags.Intersection)) {
|
||||
return isRelatedTo(source, target, RecursionFlags.Source, /*reportErrors*/ false);
|
||||
return isRelatedTo(source, target, RecursionFlags.Source, /*reportErrors*/ false) ||
|
||||
isRelatedTo(target, source, RecursionFlags.Source, /*reportErrors*/ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19541,7 +19545,7 @@ namespace ts {
|
||||
// the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
|
||||
// For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
|
||||
const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
|
||||
if (constraint && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
|
||||
if (constraint && !(constraint.flags & TypeFlags.Never) && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
|
||||
// TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
|
||||
result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
|
||||
}
|
||||
@ -25322,25 +25326,11 @@ namespace ts {
|
||||
assumeTrue = !assumeTrue;
|
||||
}
|
||||
const valueType = getTypeOfExpression(value);
|
||||
if (((type.flags & TypeFlags.Unknown) || isEmptyAnonymousObjectType(type) && !(valueType.flags & TypeFlags.Nullable)) &&
|
||||
assumeTrue &&
|
||||
(operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)
|
||||
) {
|
||||
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) {
|
||||
return valueType;
|
||||
}
|
||||
if (valueType.flags & TypeFlags.Object) {
|
||||
return nonPrimitiveType;
|
||||
}
|
||||
if (type.flags & TypeFlags.Unknown) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken;
|
||||
if (valueType.flags & TypeFlags.Nullable) {
|
||||
if (!strictNullChecks) {
|
||||
return type;
|
||||
}
|
||||
const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken;
|
||||
const facts = doubleEquals ?
|
||||
assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull :
|
||||
valueType.flags & TypeFlags.Null ?
|
||||
@ -25349,10 +25339,16 @@ namespace ts {
|
||||
return getAdjustedTypeWithFacts(type, facts);
|
||||
}
|
||||
if (assumeTrue) {
|
||||
const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ?
|
||||
t => areTypesComparable(t, valueType) || isCoercibleUnderDoubleEquals(t, valueType) :
|
||||
t => areTypesComparable(t, valueType);
|
||||
return replacePrimitivesWithLiterals(filterType(type, filterFn), valueType);
|
||||
if (!doubleEquals && (type.flags & TypeFlags.Unknown || someType(type, isEmptyAnonymousObjectType))) {
|
||||
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive) || isEmptyAnonymousObjectType(valueType)) {
|
||||
return valueType;
|
||||
}
|
||||
if (valueType.flags & TypeFlags.Object) {
|
||||
return nonPrimitiveType;
|
||||
}
|
||||
}
|
||||
const filteredType = filterType(type, t => areTypesComparable(t, valueType) || doubleEquals && isCoercibleUnderDoubleEquals(t, valueType));
|
||||
return replacePrimitivesWithLiterals(filteredType, valueType);
|
||||
}
|
||||
if (isUnitType(valueType)) {
|
||||
return filterType(type, t => !(isUnitLikeType(t) && areTypesComparable(t, valueType)));
|
||||
|
||||
@ -1243,9 +1243,9 @@ function f15(o: Thing | undefined, value: number) {
|
||||
}
|
||||
else {
|
||||
o.foo;
|
||||
>o.foo : number
|
||||
>o.foo : string | number
|
||||
>o : Thing
|
||||
>foo : number
|
||||
>foo : string | number
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -59,11 +59,11 @@ function f4<T>(x: T & 1 | T & 2) {
|
||||
|
||||
case 1: x; break; // T & 1
|
||||
>1 : 1
|
||||
>x : (T & 1) | (T & 2)
|
||||
>x : T & 1
|
||||
|
||||
case 2: x; break; // T & 2
|
||||
>2 : 2
|
||||
>x : (T & 1) | (T & 2)
|
||||
>x : T & 2
|
||||
|
||||
default: x; // Should narrow to never
|
||||
>x : never
|
||||
|
||||
@ -316,4 +316,105 @@ tests/cases/conformance/types/unknown/unknownControlFlow.ts(293,5): error TS2345
|
||||
type NullableFoo = Foo | undefined;
|
||||
|
||||
type Bar<T extends NullableFoo> = NonNullable<T>[string];
|
||||
|
||||
// Generics and intersections with {}
|
||||
|
||||
function fx0<T>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx1<T extends unknown>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx2<T extends {}>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx3<T extends {} | undefined>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx4<T extends {} | null>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx5<T extends {} | null | undefined>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
// Double-equals narrowing
|
||||
|
||||
function fx10(x: string | number, y: number) {
|
||||
if (x == y) {
|
||||
x; // string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
}
|
||||
if (x != y) {
|
||||
x; // string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #50706
|
||||
|
||||
function SendBlob(encoding: unknown) {
|
||||
if (encoding !== undefined && encoding !== 'utf8') {
|
||||
throw new Error('encoding');
|
||||
}
|
||||
encoding;
|
||||
};
|
||||
|
||||
function doSomething1<T extends unknown>(value: T): T {
|
||||
if (value === undefined) {
|
||||
return value;
|
||||
}
|
||||
if (value === 42) {
|
||||
throw Error('Meaning of life value');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function doSomething2(value: unknown): void {
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
if (value === 42) {
|
||||
value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,6 +299,107 @@ type Foo = { [key: string]: unknown };
|
||||
type NullableFoo = Foo | undefined;
|
||||
|
||||
type Bar<T extends NullableFoo> = NonNullable<T>[string];
|
||||
|
||||
// Generics and intersections with {}
|
||||
|
||||
function fx0<T>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx1<T extends unknown>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx2<T extends {}>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx3<T extends {} | undefined>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx4<T extends {} | null>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx5<T extends {} | null | undefined>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
// Double-equals narrowing
|
||||
|
||||
function fx10(x: string | number, y: number) {
|
||||
if (x == y) {
|
||||
x; // string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
}
|
||||
if (x != y) {
|
||||
x; // string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #50706
|
||||
|
||||
function SendBlob(encoding: unknown) {
|
||||
if (encoding !== undefined && encoding !== 'utf8') {
|
||||
throw new Error('encoding');
|
||||
}
|
||||
encoding;
|
||||
};
|
||||
|
||||
function doSomething1<T extends unknown>(value: T): T {
|
||||
if (value === undefined) {
|
||||
return value;
|
||||
}
|
||||
if (value === 42) {
|
||||
throw Error('Meaning of life value');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function doSomething2(value: unknown): void {
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
if (value === 42) {
|
||||
value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [unknownControlFlow.js]
|
||||
@ -552,6 +653,95 @@ ff1(null, 'foo'); // Error
|
||||
ff2(null, 'foo'); // Error
|
||||
ff3(null, 'foo');
|
||||
ff4(null, 'foo'); // Error
|
||||
// Generics and intersections with {}
|
||||
function fx0(value) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
function fx1(value) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
function fx2(value) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
function fx3(value) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
function fx4(value) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
function fx5(value) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
// Double-equals narrowing
|
||||
function fx10(x, y) {
|
||||
if (x == y) {
|
||||
x; // string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
}
|
||||
if (x != y) {
|
||||
x; // string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
}
|
||||
}
|
||||
// Repros from #50706
|
||||
function SendBlob(encoding) {
|
||||
if (encoding !== undefined && encoding !== 'utf8') {
|
||||
throw new Error('encoding');
|
||||
}
|
||||
encoding;
|
||||
}
|
||||
;
|
||||
function doSomething1(value) {
|
||||
if (value === undefined) {
|
||||
return value;
|
||||
}
|
||||
if (value === 42) {
|
||||
throw Error('Meaning of life value');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function doSomething2(value) {
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
if (value === 42) {
|
||||
value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [unknownControlFlow.d.ts]
|
||||
@ -601,3 +791,13 @@ type Foo = {
|
||||
};
|
||||
type NullableFoo = Foo | undefined;
|
||||
type Bar<T extends NullableFoo> = NonNullable<T>[string];
|
||||
declare function fx0<T>(value: T & ({} | null)): void;
|
||||
declare function fx1<T extends unknown>(value: T & ({} | null)): void;
|
||||
declare function fx2<T extends {}>(value: T & ({} | null)): void;
|
||||
declare function fx3<T extends {} | undefined>(value: T & ({} | null)): void;
|
||||
declare function fx4<T extends {} | null>(value: T & ({} | null)): void;
|
||||
declare function fx5<T extends {} | null | undefined>(value: T & ({} | null)): void;
|
||||
declare function fx10(x: string | number, y: number): void;
|
||||
declare function SendBlob(encoding: unknown): void;
|
||||
declare function doSomething1<T extends unknown>(value: T): T;
|
||||
declare function doSomething2(value: unknown): void;
|
||||
|
||||
@ -721,3 +721,205 @@ type Bar<T extends NullableFoo> = NonNullable<T>[string];
|
||||
>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 299, 9))
|
||||
|
||||
// Generics and intersections with {}
|
||||
|
||||
function fx0<T>(value: T & ({} | null)) {
|
||||
>fx0 : Symbol(fx0, Decl(unknownControlFlow.ts, 299, 57))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 303, 13))
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 303, 16))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 303, 13))
|
||||
|
||||
if (value === 42) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 303, 16))
|
||||
|
||||
value; // T & {}
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 303, 16))
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 303, 16))
|
||||
}
|
||||
}
|
||||
|
||||
function fx1<T extends unknown>(value: T & ({} | null)) {
|
||||
>fx1 : Symbol(fx1, Decl(unknownControlFlow.ts, 310, 1))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 312, 13))
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 312, 32))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 312, 13))
|
||||
|
||||
if (value === 42) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 312, 32))
|
||||
|
||||
value; // T & {}
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 312, 32))
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 312, 32))
|
||||
}
|
||||
}
|
||||
|
||||
function fx2<T extends {}>(value: T & ({} | null)) {
|
||||
>fx2 : Symbol(fx2, Decl(unknownControlFlow.ts, 319, 1))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 321, 13))
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 321, 27))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 321, 13))
|
||||
|
||||
if (value === 42) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 321, 27))
|
||||
|
||||
value; // T & {}
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 321, 27))
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 321, 27))
|
||||
}
|
||||
}
|
||||
|
||||
function fx3<T extends {} | undefined>(value: T & ({} | null)) {
|
||||
>fx3 : Symbol(fx3, Decl(unknownControlFlow.ts, 328, 1))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 330, 13))
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 330, 39))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 330, 13))
|
||||
|
||||
if (value === 42) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 330, 39))
|
||||
|
||||
value; // T & {}
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 330, 39))
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 330, 39))
|
||||
}
|
||||
}
|
||||
|
||||
function fx4<T extends {} | null>(value: T & ({} | null)) {
|
||||
>fx4 : Symbol(fx4, Decl(unknownControlFlow.ts, 337, 1))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 339, 13))
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 339, 34))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 339, 13))
|
||||
|
||||
if (value === 42) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 339, 34))
|
||||
|
||||
value; // T & {}
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 339, 34))
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 339, 34))
|
||||
}
|
||||
}
|
||||
|
||||
function fx5<T extends {} | null | undefined>(value: T & ({} | null)) {
|
||||
>fx5 : Symbol(fx5, Decl(unknownControlFlow.ts, 346, 1))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 348, 13))
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 348, 46))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 348, 13))
|
||||
|
||||
if (value === 42) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 348, 46))
|
||||
|
||||
value; // T & {}
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 348, 46))
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 348, 46))
|
||||
}
|
||||
}
|
||||
|
||||
// Double-equals narrowing
|
||||
|
||||
function fx10(x: string | number, y: number) {
|
||||
>fx10 : Symbol(fx10, Decl(unknownControlFlow.ts, 355, 1))
|
||||
>x : Symbol(x, Decl(unknownControlFlow.ts, 359, 14))
|
||||
>y : Symbol(y, Decl(unknownControlFlow.ts, 359, 33))
|
||||
|
||||
if (x == y) {
|
||||
>x : Symbol(x, Decl(unknownControlFlow.ts, 359, 14))
|
||||
>y : Symbol(y, Decl(unknownControlFlow.ts, 359, 33))
|
||||
|
||||
x; // string | number
|
||||
>x : Symbol(x, Decl(unknownControlFlow.ts, 359, 14))
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
>x : Symbol(x, Decl(unknownControlFlow.ts, 359, 14))
|
||||
}
|
||||
if (x != y) {
|
||||
>x : Symbol(x, Decl(unknownControlFlow.ts, 359, 14))
|
||||
>y : Symbol(y, Decl(unknownControlFlow.ts, 359, 33))
|
||||
|
||||
x; // string | number
|
||||
>x : Symbol(x, Decl(unknownControlFlow.ts, 359, 14))
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
>x : Symbol(x, Decl(unknownControlFlow.ts, 359, 14))
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #50706
|
||||
|
||||
function SendBlob(encoding: unknown) {
|
||||
>SendBlob : Symbol(SendBlob, Decl(unknownControlFlow.ts, 372, 1))
|
||||
>encoding : Symbol(encoding, Decl(unknownControlFlow.ts, 376, 18))
|
||||
|
||||
if (encoding !== undefined && encoding !== 'utf8') {
|
||||
>encoding : Symbol(encoding, Decl(unknownControlFlow.ts, 376, 18))
|
||||
>undefined : Symbol(undefined)
|
||||
>encoding : Symbol(encoding, Decl(unknownControlFlow.ts, 376, 18))
|
||||
|
||||
throw new Error('encoding');
|
||||
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
encoding;
|
||||
>encoding : Symbol(encoding, Decl(unknownControlFlow.ts, 376, 18))
|
||||
|
||||
};
|
||||
|
||||
function doSomething1<T extends unknown>(value: T): T {
|
||||
>doSomething1 : Symbol(doSomething1, Decl(unknownControlFlow.ts, 381, 2))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 383, 22))
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 383, 41))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 383, 22))
|
||||
>T : Symbol(T, Decl(unknownControlFlow.ts, 383, 22))
|
||||
|
||||
if (value === undefined) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 383, 41))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
return value;
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 383, 41))
|
||||
}
|
||||
if (value === 42) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 383, 41))
|
||||
|
||||
throw Error('Meaning of life value');
|
||||
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
return value;
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 383, 41))
|
||||
}
|
||||
|
||||
function doSomething2(value: unknown): void {
|
||||
>doSomething2 : Symbol(doSomething2, Decl(unknownControlFlow.ts, 391, 1))
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 393, 22))
|
||||
|
||||
if (value === undefined) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 393, 22))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
return;
|
||||
}
|
||||
if (value === 42) {
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 393, 22))
|
||||
|
||||
value;
|
||||
>value : Symbol(value, Decl(unknownControlFlow.ts, 393, 22))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -802,3 +802,226 @@ type NullableFoo = Foo | undefined;
|
||||
type Bar<T extends NullableFoo> = NonNullable<T>[string];
|
||||
>Bar : Bar<T>
|
||||
|
||||
// Generics and intersections with {}
|
||||
|
||||
function fx0<T>(value: T & ({} | null)) {
|
||||
>fx0 : <T>(value: T & ({} | null)) => void
|
||||
>value : T & ({} | null)
|
||||
>null : null
|
||||
|
||||
if (value === 42) {
|
||||
>value === 42 : boolean
|
||||
>value : T & ({} | null)
|
||||
>42 : 42
|
||||
|
||||
value; // T & {}
|
||||
>value : T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx1<T extends unknown>(value: T & ({} | null)) {
|
||||
>fx1 : <T extends unknown>(value: T & ({} | null)) => void
|
||||
>value : T & ({} | null)
|
||||
>null : null
|
||||
|
||||
if (value === 42) {
|
||||
>value === 42 : boolean
|
||||
>value : T & ({} | null)
|
||||
>42 : 42
|
||||
|
||||
value; // T & {}
|
||||
>value : T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx2<T extends {}>(value: T & ({} | null)) {
|
||||
>fx2 : <T extends {}>(value: T & ({} | null)) => void
|
||||
>value : T & ({} | null)
|
||||
>null : null
|
||||
|
||||
if (value === 42) {
|
||||
>value === 42 : boolean
|
||||
>value : T & ({} | null)
|
||||
>42 : 42
|
||||
|
||||
value; // T & {}
|
||||
>value : T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx3<T extends {} | undefined>(value: T & ({} | null)) {
|
||||
>fx3 : <T extends {} | undefined>(value: T & ({} | null)) => void
|
||||
>value : T & ({} | null)
|
||||
>null : null
|
||||
|
||||
if (value === 42) {
|
||||
>value === 42 : boolean
|
||||
>value : T & ({} | null)
|
||||
>42 : 42
|
||||
|
||||
value; // T & {}
|
||||
>value : T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx4<T extends {} | null>(value: T & ({} | null)) {
|
||||
>fx4 : <T extends {} | null>(value: T & ({} | null)) => void
|
||||
>null : null
|
||||
>value : T & ({} | null)
|
||||
>null : null
|
||||
|
||||
if (value === 42) {
|
||||
>value === 42 : boolean
|
||||
>value : T & ({} | null)
|
||||
>42 : 42
|
||||
|
||||
value; // T & {}
|
||||
>value : T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx5<T extends {} | null | undefined>(value: T & ({} | null)) {
|
||||
>fx5 : <T extends {} | null | undefined>(value: T & ({} | null)) => void
|
||||
>null : null
|
||||
>value : T & ({} | null)
|
||||
>null : null
|
||||
|
||||
if (value === 42) {
|
||||
>value === 42 : boolean
|
||||
>value : T & ({} | null)
|
||||
>42 : 42
|
||||
|
||||
value; // T & {}
|
||||
>value : T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
>value : T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
// Double-equals narrowing
|
||||
|
||||
function fx10(x: string | number, y: number) {
|
||||
>fx10 : (x: string | number, y: number) => void
|
||||
>x : string | number
|
||||
>y : number
|
||||
|
||||
if (x == y) {
|
||||
>x == y : boolean
|
||||
>x : string | number
|
||||
>y : number
|
||||
|
||||
x; // string | number
|
||||
>x : string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
>x : string | number
|
||||
}
|
||||
if (x != y) {
|
||||
>x != y : boolean
|
||||
>x : string | number
|
||||
>y : number
|
||||
|
||||
x; // string | number
|
||||
>x : string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
>x : string | number
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #50706
|
||||
|
||||
function SendBlob(encoding: unknown) {
|
||||
>SendBlob : (encoding: unknown) => void
|
||||
>encoding : unknown
|
||||
|
||||
if (encoding !== undefined && encoding !== 'utf8') {
|
||||
>encoding !== undefined && encoding !== 'utf8' : boolean
|
||||
>encoding !== undefined : boolean
|
||||
>encoding : unknown
|
||||
>undefined : undefined
|
||||
>encoding !== 'utf8' : boolean
|
||||
>encoding : {} | null
|
||||
>'utf8' : "utf8"
|
||||
|
||||
throw new Error('encoding');
|
||||
>new Error('encoding') : Error
|
||||
>Error : ErrorConstructor
|
||||
>'encoding' : "encoding"
|
||||
}
|
||||
encoding;
|
||||
>encoding : "utf8" | undefined
|
||||
|
||||
};
|
||||
|
||||
function doSomething1<T extends unknown>(value: T): T {
|
||||
>doSomething1 : <T extends unknown>(value: T) => T
|
||||
>value : T
|
||||
|
||||
if (value === undefined) {
|
||||
>value === undefined : boolean
|
||||
>value : T
|
||||
>undefined : undefined
|
||||
|
||||
return value;
|
||||
>value : T
|
||||
}
|
||||
if (value === 42) {
|
||||
>value === 42 : boolean
|
||||
>value : T & ({} | null)
|
||||
>42 : 42
|
||||
|
||||
throw Error('Meaning of life value');
|
||||
>Error('Meaning of life value') : Error
|
||||
>Error : ErrorConstructor
|
||||
>'Meaning of life value' : "Meaning of life value"
|
||||
}
|
||||
return value;
|
||||
>value : T & ({} | null)
|
||||
}
|
||||
|
||||
function doSomething2(value: unknown): void {
|
||||
>doSomething2 : (value: unknown) => void
|
||||
>value : unknown
|
||||
|
||||
if (value === undefined) {
|
||||
>value === undefined : boolean
|
||||
>value : unknown
|
||||
>undefined : undefined
|
||||
|
||||
return;
|
||||
}
|
||||
if (value === 42) {
|
||||
>value === 42 : boolean
|
||||
>value : {} | null
|
||||
>42 : 42
|
||||
|
||||
value;
|
||||
>value : 42
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -301,3 +301,104 @@ type Foo = { [key: string]: unknown };
|
||||
type NullableFoo = Foo | undefined;
|
||||
|
||||
type Bar<T extends NullableFoo> = NonNullable<T>[string];
|
||||
|
||||
// Generics and intersections with {}
|
||||
|
||||
function fx0<T>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx1<T extends unknown>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx2<T extends {}>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx3<T extends {} | undefined>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx4<T extends {} | null>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
function fx5<T extends {} | null | undefined>(value: T & ({} | null)) {
|
||||
if (value === 42) {
|
||||
value; // T & {}
|
||||
}
|
||||
else {
|
||||
value; // T & ({} | null)
|
||||
}
|
||||
}
|
||||
|
||||
// Double-equals narrowing
|
||||
|
||||
function fx10(x: string | number, y: number) {
|
||||
if (x == y) {
|
||||
x; // string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
}
|
||||
if (x != y) {
|
||||
x; // string | number
|
||||
}
|
||||
else {
|
||||
x; // string | number
|
||||
}
|
||||
}
|
||||
|
||||
// Repros from #50706
|
||||
|
||||
function SendBlob(encoding: unknown) {
|
||||
if (encoding !== undefined && encoding !== 'utf8') {
|
||||
throw new Error('encoding');
|
||||
}
|
||||
encoding;
|
||||
};
|
||||
|
||||
function doSomething1<T extends unknown>(value: T): T {
|
||||
if (value === undefined) {
|
||||
return value;
|
||||
}
|
||||
if (value === 42) {
|
||||
throw Error('Meaning of life value');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function doSomething2(value: unknown): void {
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
if (value === 42) {
|
||||
value;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user