Cherry-pick PR #42846 into release-4.2 (#42852)

Component commits:
0edae127ae Reduce void | undefined only in conjunction with subtype reduction

6b487a6db5 Accept new baselines

e7b6601de7 Add regression test

Co-authored-by: Anders Hejlsberg <andersh@microsoft.com>
This commit is contained in:
TypeScript Bot 2021-02-17 17:48:43 -08:00 committed by GitHub
parent 16f2556473
commit 57ba6ddaee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 129 additions and 23 deletions

View File

@ -13358,7 +13358,7 @@ namespace ts {
return true;
}
function removeRedundantLiteralTypes(types: Type[], includes: TypeFlags) {
function removeRedundantLiteralTypes(types: Type[], includes: TypeFlags, reduceVoidUndefined: boolean) {
let i = types.length;
while (i > 0) {
i--;
@ -13369,7 +13369,7 @@ namespace ts {
flags & TypeFlags.NumberLiteral && includes & TypeFlags.Number ||
flags & TypeFlags.BigIntLiteral && includes & TypeFlags.BigInt ||
flags & TypeFlags.UniqueESSymbol && includes & TypeFlags.ESSymbol ||
flags & TypeFlags.Undefined && includes & TypeFlags.Void ||
reduceVoidUndefined && flags & TypeFlags.Undefined && includes & TypeFlags.Void ||
isFreshLiteralType(t) && containsType(types, (<LiteralType>t).regularType);
if (remove) {
orderedRemoveItemAt(types, i);
@ -13437,7 +13437,7 @@ namespace ts {
}
if (unionReduction & (UnionReduction.Literal | UnionReduction.Subtype)) {
if (includes & (TypeFlags.Literal | TypeFlags.UniqueESSymbol) || includes & TypeFlags.Void && includes & TypeFlags.Undefined) {
removeRedundantLiteralTypes(typeSet, includes);
removeRedundantLiteralTypes(typeSet, includes, !!(unionReduction & UnionReduction.Subtype));
}
if (includes & TypeFlags.StringLiteral && includes & TypeFlags.TemplateLiteral) {
removeStringLiteralsMatchedByTemplateLiterals(typeSet);

View File

@ -260,7 +260,7 @@ declare const o5: <T>() => undefined | (() => void);
>o5 : <T>() => undefined | (() => void)
o5<number>()?.();
>o5<number>()?.() : void
>o5<number>()?.() : void | undefined
>o5<number>() : (() => void) | undefined
>o5 : <T>() => (() => void) | undefined

View File

@ -29,7 +29,7 @@ if (value) {
}
value?.foo("a");
>value?.foo("a") : void
>value?.foo("a") : void | undefined
>value?.foo : (<T>(this: T, arg: keyof T) => void) | undefined
>value : Y | undefined
>foo : (<T>(this: T, arg: keyof T) => void) | undefined

View File

@ -595,7 +595,7 @@ function f01(x: unknown) {
>true : true
maybeIsString?.(x);
>maybeIsString?.(x) : void
>maybeIsString?.(x) : void | undefined
>maybeIsString : ((value: unknown) => asserts value is string) | undefined
>x : unknown

View File

@ -13,7 +13,7 @@ class C extends B {
>body : () => void
super.m && super.m();
>super.m && super.m() : void
>super.m && super.m() : void | undefined
>super.m : (() => void) | undefined
>super : B
>m : (() => void) | undefined

View File

@ -343,7 +343,7 @@ const u: U = {} as any;
>{} : {}
u.a && u.b && f(u.a, u.b);
>u.a && u.b && f(u.a, u.b) : void | ""
>u.a && u.b && f(u.a, u.b) : void | "" | undefined
>u.a && u.b : string | undefined
>u.a : string | undefined
>u : U
@ -361,7 +361,7 @@ u.a && u.b && f(u.a, u.b);
>b : string
u.b && u.a && f(u.a, u.b);
>u.b && u.a && f(u.a, u.b) : void | ""
>u.b && u.a && f(u.a, u.b) : void | "" | undefined
>u.b && u.a : string | undefined
>u.b : string | undefined
>u : U

View File

@ -888,8 +888,8 @@ const p75 = p.then(() => undefined, () => null);
>null : null
const p76 = p.then(() => undefined, () => {});
>p76 : Promise<void>
>p.then(() => undefined, () => {}) : Promise<void>
>p76 : Promise<void | undefined>
>p.then(() => undefined, () => {}) : Promise<void | undefined>
>p.then : <TResult1 = boolean, TResult2 = never>(onfulfilled?: ((value: boolean) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
>p : Promise<boolean>
>then : <TResult1 = boolean, TResult2 = never>(onfulfilled?: ((value: boolean) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
@ -1092,8 +1092,8 @@ const p93 = p.then(() => {}, () => x);
>x : any
const p94 = p.then(() => {}, () => undefined);
>p94 : Promise<void>
>p.then(() => {}, () => undefined) : Promise<void>
>p94 : Promise<void | undefined>
>p.then(() => {}, () => undefined) : Promise<void | undefined>
>p.then : <TResult1 = boolean, TResult2 = never>(onfulfilled?: ((value: boolean) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
>p : Promise<boolean>
>then : <TResult1 = boolean, TResult2 = never>(onfulfilled?: ((value: boolean) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>

View File

@ -11,20 +11,20 @@ class Derived extends Base {
>Base : Base
method() {
>method : () => void
>method : () => void | undefined
return super.method?.();
>super.method?.() : void
>super.method?.() : void | undefined
>super.method : (() => void) | undefined
>super : Base
>method : (() => void) | undefined
}
async asyncMethod() {
>asyncMethod : () => Promise<void>
>asyncMethod : () => Promise<void | undefined>
return super.method?.();
>super.method?.() : void
>super.method?.() : void | undefined
>super.method : (() => void) | undefined
>super : Base
>method : (() => void) | undefined

View File

@ -9,7 +9,7 @@ class C {
>other : () => void
this.method?.();
>this.method?.() : void
>this.method?.() : void | undefined
>this.method : (() => void) | undefined
>this : this
>method : (() => void) | undefined

View File

@ -60,7 +60,7 @@ function test(required1: () => boolean, required2: () => boolean, b: boolean, op
// ok
optional && console.log('optional');
>optional && console.log('optional') : void
>optional && console.log('optional') : void | undefined
>optional : (() => boolean) | undefined
>console.log('optional') : void
>console.log : (...data: any[]) => void
@ -70,7 +70,7 @@ function test(required1: () => boolean, required2: () => boolean, b: boolean, op
// ok
1 && optional && console.log('optional');
>1 && optional && console.log('optional') : void
>1 && optional && console.log('optional') : void | undefined
>1 && optional : (() => boolean) | undefined
>1 : 1
>optional : (() => boolean) | undefined
@ -441,7 +441,7 @@ class Foo {
// ok
1 && this.optional && console.log('optional');
>1 && this.optional && console.log('optional') : void
>1 && this.optional && console.log('optional') : void | undefined
>1 && this.optional : (() => boolean) | undefined
>1 : 1
>this.optional : (() => boolean) | undefined

View File

@ -16,7 +16,7 @@ class A<P extends Partial<Foo>> {
>doSomething : () => void
this.props.foo && this.props.foo()
>this.props.foo && this.props.foo() : void
>this.props.foo && this.props.foo() : void | undefined
>this.props.foo : P["foo"] | undefined
>this.props : Readonly<P>
>this : this

View File

@ -51,7 +51,7 @@ function bad<P extends Props>(props: Readonly<P>) {
// ERROR HERE!!!
// Type R in signature of safeInvoke incorrectly inferred as {} instead of void!
safeInvoke(props.onBar, "blah");
>safeInvoke(props.onBar, "blah") : void
>safeInvoke(props.onBar, "blah") : void | undefined
>safeInvoke : <A1, R>(func: ((arg1: A1) => R) | null | undefined, arg1: A1) => R | undefined
>props.onBar : P["onBar"] | undefined
>props : Readonly<P>

View File

@ -0,0 +1,23 @@
//// [voidUndefinedReduction.ts]
// Repro from #42786
function isDefined<T>(value: T | undefined | null | void): value is T {
return value !== undefined && value !== null;
}
declare const foo: string | undefined;
if (isDefined(foo)) {
console.log(foo.toUpperCase());
}
//// [voidUndefinedReduction.js]
"use strict";
// Repro from #42786
function isDefined(value) {
return value !== undefined && value !== null;
}
if (isDefined(foo)) {
console.log(foo.toUpperCase());
}

View File

@ -0,0 +1,33 @@
=== tests/cases/compiler/voidUndefinedReduction.ts ===
// Repro from #42786
function isDefined<T>(value: T | undefined | null | void): value is T {
>isDefined : Symbol(isDefined, Decl(voidUndefinedReduction.ts, 0, 0))
>T : Symbol(T, Decl(voidUndefinedReduction.ts, 2, 19))
>value : Symbol(value, Decl(voidUndefinedReduction.ts, 2, 22))
>T : Symbol(T, Decl(voidUndefinedReduction.ts, 2, 19))
>value : Symbol(value, Decl(voidUndefinedReduction.ts, 2, 22))
>T : Symbol(T, Decl(voidUndefinedReduction.ts, 2, 19))
return value !== undefined && value !== null;
>value : Symbol(value, Decl(voidUndefinedReduction.ts, 2, 22))
>undefined : Symbol(undefined)
>value : Symbol(value, Decl(voidUndefinedReduction.ts, 2, 22))
}
declare const foo: string | undefined;
>foo : Symbol(foo, Decl(voidUndefinedReduction.ts, 6, 13))
if (isDefined(foo)) {
>isDefined : Symbol(isDefined, Decl(voidUndefinedReduction.ts, 0, 0))
>foo : Symbol(foo, Decl(voidUndefinedReduction.ts, 6, 13))
console.log(foo.toUpperCase());
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>foo.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
>foo : Symbol(foo, Decl(voidUndefinedReduction.ts, 6, 13))
>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --))
}

View File

@ -0,0 +1,37 @@
=== tests/cases/compiler/voidUndefinedReduction.ts ===
// Repro from #42786
function isDefined<T>(value: T | undefined | null | void): value is T {
>isDefined : <T>(value: T | undefined | null | void) => value is T
>value : void | T | null | undefined
>null : null
return value !== undefined && value !== null;
>value !== undefined && value !== null : boolean
>value !== undefined : boolean
>value : void | T | null | undefined
>undefined : undefined
>value !== null : boolean
>value : T | null
>null : null
}
declare const foo: string | undefined;
>foo : string | undefined
if (isDefined(foo)) {
>isDefined(foo) : boolean
>isDefined : <T>(value: void | T | null | undefined) => value is T
>foo : string | undefined
console.log(foo.toUpperCase());
>console.log(foo.toUpperCase()) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>foo.toUpperCase() : string
>foo.toUpperCase : () => string
>foo : string
>toUpperCase : () => string
}

View File

@ -0,0 +1,13 @@
// @strict: true
// Repro from #42786
function isDefined<T>(value: T | undefined | null | void): value is T {
return value !== undefined && value !== null;
}
declare const foo: string | undefined;
if (isDefined(foo)) {
console.log(foo.toUpperCase());
}