mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 10:46:28 -05:00
Narrow unknown under inequality when assumed false (#33488)
This commit is contained in:
committed by
Ryan Cavanaugh
parent
344dba8809
commit
1c20aa0b1a
@@ -17453,7 +17453,7 @@ namespace ts {
|
||||
assumeTrue = !assumeTrue;
|
||||
}
|
||||
const valueType = getTypeOfExpression(value);
|
||||
if ((type.flags & TypeFlags.Unknown) && (operator === SyntaxKind.EqualsEqualsEqualsToken) && assumeTrue) {
|
||||
if ((type.flags & TypeFlags.Unknown) && assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) {
|
||||
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) {
|
||||
return valueType;
|
||||
}
|
||||
|
||||
@@ -228,4 +228,41 @@ tests/cases/conformance/types/unknown/unknownType2.ts(216,13): error TS2322: Typ
|
||||
// Arguably this should be never.
|
||||
type End = isTrue<isUnknown<typeof x>>
|
||||
}
|
||||
|
||||
// Repro from #33483
|
||||
|
||||
function f2(x: unknown): string | undefined {
|
||||
if (x !== undefined && typeof x !== 'string') {
|
||||
throw new Error();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
function notNotEquals(u: unknown) {
|
||||
if (u !== NumberEnum) { }
|
||||
else {
|
||||
const o: object = u;
|
||||
}
|
||||
|
||||
if (u !== NumberEnum.A) { }
|
||||
else {
|
||||
const a: NumberEnum.A = u;
|
||||
}
|
||||
|
||||
|
||||
if (u !== NumberEnum.A && u !== NumberEnum.B && u !== StringEnum.A) { }
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
}
|
||||
|
||||
// equivalent to
|
||||
if (!(u === NumberEnum.A || u === NumberEnum.B || u === StringEnum.A)) { }
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -221,6 +221,43 @@ function switchResponseWrong(x: unknown): SomeResponse {
|
||||
// Arguably this should be never.
|
||||
type End = isTrue<isUnknown<typeof x>>
|
||||
}
|
||||
|
||||
// Repro from #33483
|
||||
|
||||
function f2(x: unknown): string | undefined {
|
||||
if (x !== undefined && typeof x !== 'string') {
|
||||
throw new Error();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
function notNotEquals(u: unknown) {
|
||||
if (u !== NumberEnum) { }
|
||||
else {
|
||||
const o: object = u;
|
||||
}
|
||||
|
||||
if (u !== NumberEnum.A) { }
|
||||
else {
|
||||
const a: NumberEnum.A = u;
|
||||
}
|
||||
|
||||
|
||||
if (u !== NumberEnum.A && u !== NumberEnum.B && u !== StringEnum.A) { }
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
}
|
||||
|
||||
// equivalent to
|
||||
if (!(u === NumberEnum.A || u === NumberEnum.B || u === StringEnum.A)) { }
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//// [unknownType2.js]
|
||||
@@ -388,3 +425,29 @@ function switchResponseWrong(x) {
|
||||
throw new Error('Can you repeat the question?');
|
||||
}
|
||||
}
|
||||
// Repro from #33483
|
||||
function f2(x) {
|
||||
if (x !== undefined && typeof x !== 'string') {
|
||||
throw new Error();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
function notNotEquals(u) {
|
||||
if (u !== NumberEnum) { }
|
||||
else {
|
||||
var o = u;
|
||||
}
|
||||
if (u !== NumberEnum.A) { }
|
||||
else {
|
||||
var a = u;
|
||||
}
|
||||
if (u !== NumberEnum.A && u !== NumberEnum.B && u !== StringEnum.A) { }
|
||||
else {
|
||||
var aOrB = u;
|
||||
}
|
||||
// equivalent to
|
||||
if (!(u === NumberEnum.A || u === NumberEnum.B || u === StringEnum.A)) { }
|
||||
else {
|
||||
var aOrB = u;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,3 +582,108 @@ function switchResponseWrong(x: unknown): SomeResponse {
|
||||
>x : Symbol(x, Decl(unknownType2.ts, 210, 29))
|
||||
}
|
||||
|
||||
// Repro from #33483
|
||||
|
||||
function f2(x: unknown): string | undefined {
|
||||
>f2 : Symbol(f2, Decl(unknownType2.ts, 221, 1))
|
||||
>x : Symbol(x, Decl(unknownType2.ts, 225, 12))
|
||||
|
||||
if (x !== undefined && typeof x !== 'string') {
|
||||
>x : Symbol(x, Decl(unknownType2.ts, 225, 12))
|
||||
>undefined : Symbol(undefined)
|
||||
>x : Symbol(x, Decl(unknownType2.ts, 225, 12))
|
||||
|
||||
throw new Error();
|
||||
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
return x;
|
||||
>x : Symbol(x, Decl(unknownType2.ts, 225, 12))
|
||||
}
|
||||
|
||||
function notNotEquals(u: unknown) {
|
||||
>notNotEquals : Symbol(notNotEquals, Decl(unknownType2.ts, 230, 1))
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
|
||||
if (u !== NumberEnum) { }
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
|
||||
else {
|
||||
const o: object = u;
|
||||
>o : Symbol(o, Decl(unknownType2.ts, 235, 13))
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
}
|
||||
|
||||
if (u !== NumberEnum.A) { }
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
>NumberEnum.A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 92, 17))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 92, 17))
|
||||
|
||||
else {
|
||||
const a: NumberEnum.A = u;
|
||||
>a : Symbol(a, Decl(unknownType2.ts, 240, 13))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 92, 17))
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
}
|
||||
|
||||
|
||||
if (u !== NumberEnum.A && u !== NumberEnum.B && u !== StringEnum.A) { }
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
>NumberEnum.A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 92, 17))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 92, 17))
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
>NumberEnum.B : Symbol(NumberEnum.B, Decl(unknownType2.ts, 93, 6))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>B : Symbol(NumberEnum.B, Decl(unknownType2.ts, 93, 6))
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
>StringEnum.A : Symbol(StringEnum.A, Decl(unknownType2.ts, 98, 17))
|
||||
>StringEnum : Symbol(StringEnum, Decl(unknownType2.ts, 96, 1))
|
||||
>A : Symbol(StringEnum.A, Decl(unknownType2.ts, 98, 17))
|
||||
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
>aOrB : Symbol(aOrB, Decl(unknownType2.ts, 246, 13))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 92, 17))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>B : Symbol(NumberEnum.B, Decl(unknownType2.ts, 93, 6))
|
||||
>StringEnum : Symbol(StringEnum, Decl(unknownType2.ts, 96, 1))
|
||||
>A : Symbol(StringEnum.A, Decl(unknownType2.ts, 98, 17))
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
}
|
||||
|
||||
// equivalent to
|
||||
if (!(u === NumberEnum.A || u === NumberEnum.B || u === StringEnum.A)) { }
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
>NumberEnum.A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 92, 17))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 92, 17))
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
>NumberEnum.B : Symbol(NumberEnum.B, Decl(unknownType2.ts, 93, 6))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>B : Symbol(NumberEnum.B, Decl(unknownType2.ts, 93, 6))
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
>StringEnum.A : Symbol(StringEnum.A, Decl(unknownType2.ts, 98, 17))
|
||||
>StringEnum : Symbol(StringEnum, Decl(unknownType2.ts, 96, 1))
|
||||
>A : Symbol(StringEnum.A, Decl(unknownType2.ts, 98, 17))
|
||||
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
>aOrB : Symbol(aOrB, Decl(unknownType2.ts, 252, 13))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>A : Symbol(NumberEnum.A, Decl(unknownType2.ts, 92, 17))
|
||||
>NumberEnum : Symbol(NumberEnum, Decl(unknownType2.ts, 90, 1))
|
||||
>B : Symbol(NumberEnum.B, Decl(unknownType2.ts, 93, 6))
|
||||
>StringEnum : Symbol(StringEnum, Decl(unknownType2.ts, 96, 1))
|
||||
>A : Symbol(StringEnum.A, Decl(unknownType2.ts, 98, 17))
|
||||
>u : Symbol(u, Decl(unknownType2.ts, 232, 22))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -627,3 +627,121 @@ function switchResponseWrong(x: unknown): SomeResponse {
|
||||
>x : unknown
|
||||
}
|
||||
|
||||
// Repro from #33483
|
||||
|
||||
function f2(x: unknown): string | undefined {
|
||||
>f2 : (x: unknown) => string | undefined
|
||||
>x : unknown
|
||||
|
||||
if (x !== undefined && typeof x !== 'string') {
|
||||
>x !== undefined && typeof x !== 'string' : boolean
|
||||
>x !== undefined : boolean
|
||||
>x : unknown
|
||||
>undefined : undefined
|
||||
>typeof x !== 'string' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>'string' : "string"
|
||||
|
||||
throw new Error();
|
||||
>new Error() : Error
|
||||
>Error : ErrorConstructor
|
||||
}
|
||||
return x;
|
||||
>x : string | undefined
|
||||
}
|
||||
|
||||
function notNotEquals(u: unknown) {
|
||||
>notNotEquals : (u: unknown) => void
|
||||
>u : unknown
|
||||
|
||||
if (u !== NumberEnum) { }
|
||||
>u !== NumberEnum : boolean
|
||||
>u : unknown
|
||||
>NumberEnum : typeof NumberEnum
|
||||
|
||||
else {
|
||||
const o: object = u;
|
||||
>o : object
|
||||
>u : object
|
||||
}
|
||||
|
||||
if (u !== NumberEnum.A) { }
|
||||
>u !== NumberEnum.A : boolean
|
||||
>u : unknown
|
||||
>NumberEnum.A : NumberEnum.A
|
||||
>NumberEnum : typeof NumberEnum
|
||||
>A : NumberEnum.A
|
||||
|
||||
else {
|
||||
const a: NumberEnum.A = u;
|
||||
>a : NumberEnum.A
|
||||
>NumberEnum : any
|
||||
>u : NumberEnum.A
|
||||
}
|
||||
|
||||
|
||||
if (u !== NumberEnum.A && u !== NumberEnum.B && u !== StringEnum.A) { }
|
||||
>u !== NumberEnum.A && u !== NumberEnum.B && u !== StringEnum.A : boolean
|
||||
>u !== NumberEnum.A && u !== NumberEnum.B : boolean
|
||||
>u !== NumberEnum.A : boolean
|
||||
>u : unknown
|
||||
>NumberEnum.A : NumberEnum.A
|
||||
>NumberEnum : typeof NumberEnum
|
||||
>A : NumberEnum.A
|
||||
>u !== NumberEnum.B : boolean
|
||||
>u : unknown
|
||||
>NumberEnum.B : NumberEnum.B
|
||||
>NumberEnum : typeof NumberEnum
|
||||
>B : NumberEnum.B
|
||||
>u !== StringEnum.A : boolean
|
||||
>u : unknown
|
||||
>StringEnum.A : StringEnum.A
|
||||
>StringEnum : typeof StringEnum
|
||||
>A : StringEnum.A
|
||||
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
>aOrB : NumberEnum.A | NumberEnum.B | StringEnum.A
|
||||
>NumberEnum : any
|
||||
>NumberEnum : any
|
||||
>StringEnum : any
|
||||
>u : NumberEnum.A | NumberEnum.B | StringEnum.A
|
||||
}
|
||||
|
||||
// equivalent to
|
||||
if (!(u === NumberEnum.A || u === NumberEnum.B || u === StringEnum.A)) { }
|
||||
>!(u === NumberEnum.A || u === NumberEnum.B || u === StringEnum.A) : boolean
|
||||
>(u === NumberEnum.A || u === NumberEnum.B || u === StringEnum.A) : boolean
|
||||
>u === NumberEnum.A || u === NumberEnum.B || u === StringEnum.A : boolean
|
||||
>u === NumberEnum.A || u === NumberEnum.B : boolean
|
||||
>u === NumberEnum.A : boolean
|
||||
>u : unknown
|
||||
>NumberEnum.A : NumberEnum.A
|
||||
>NumberEnum : typeof NumberEnum
|
||||
>A : NumberEnum.A
|
||||
>u === NumberEnum.B : boolean
|
||||
>u : unknown
|
||||
>NumberEnum.B : NumberEnum.B
|
||||
>NumberEnum : typeof NumberEnum
|
||||
>B : NumberEnum.B
|
||||
>u === StringEnum.A : boolean
|
||||
>u : unknown
|
||||
>StringEnum.A : StringEnum.A
|
||||
>StringEnum : typeof StringEnum
|
||||
>A : StringEnum.A
|
||||
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
>aOrB : NumberEnum.A | NumberEnum.B | StringEnum.A
|
||||
>NumberEnum : any
|
||||
>NumberEnum : any
|
||||
>StringEnum : any
|
||||
>u : NumberEnum.A | NumberEnum.B | StringEnum.A
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -222,3 +222,40 @@ function switchResponseWrong(x: unknown): SomeResponse {
|
||||
// Arguably this should be never.
|
||||
type End = isTrue<isUnknown<typeof x>>
|
||||
}
|
||||
|
||||
// Repro from #33483
|
||||
|
||||
function f2(x: unknown): string | undefined {
|
||||
if (x !== undefined && typeof x !== 'string') {
|
||||
throw new Error();
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
function notNotEquals(u: unknown) {
|
||||
if (u !== NumberEnum) { }
|
||||
else {
|
||||
const o: object = u;
|
||||
}
|
||||
|
||||
if (u !== NumberEnum.A) { }
|
||||
else {
|
||||
const a: NumberEnum.A = u;
|
||||
}
|
||||
|
||||
|
||||
if (u !== NumberEnum.A && u !== NumberEnum.B && u !== StringEnum.A) { }
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
}
|
||||
|
||||
// equivalent to
|
||||
if (!(u === NumberEnum.A || u === NumberEnum.B || u === StringEnum.A)) { }
|
||||
else {
|
||||
const aOrB: NumberEnum.A | NumberEnum.B | StringEnum.A = u;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user