Skip to content

Commit

Permalink
Cherry-pick PR microsoft#42846 into release-4.2
Browse files Browse the repository at this point in the history
Component commits:
0edae12 Reduce void | undefined only in conjunction with subtype reduction

6b487a6 Accept new baselines

e7b6601 Add regression test
  • Loading branch information
ahejlsberg authored and typescript-bot committed Feb 18, 2021
1 parent 16f2556 commit 8ed48d8
Show file tree
Hide file tree
Showing 16 changed files with 129 additions and 23 deletions.
6 changes: 3 additions & 3 deletions src/compiler/checker.ts
Expand Up @@ -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--;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/callChain.types
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/callChainInference.types
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/controlFlowOptionalChain.types
Expand Up @@ -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

Expand Down
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/discriminantPropertyCheck.types
Expand Up @@ -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
Expand All @@ -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
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/promiseTypeStrictNull.types
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/superMethodCall.types
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/thisMethodCall.types
Expand Up @@ -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
Expand Down
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/typeVariableTypeGuards.types
Expand Up @@ -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
Expand Down
Expand Up @@ -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>
Expand Down
23 changes: 23 additions & 0 deletions tests/baselines/reference/voidUndefinedReduction.js
@@ -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());
}
33 changes: 33 additions & 0 deletions tests/baselines/reference/voidUndefinedReduction.symbols
@@ -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, --, --))
}

37 changes: 37 additions & 0 deletions tests/baselines/reference/voidUndefinedReduction.types
@@ -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
}

13 changes: 13 additions & 0 deletions tests/cases/compiler/voidUndefinedReduction.ts
@@ -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());
}

0 comments on commit 8ed48d8

Please sign in to comment.