Improve indexed access inferences (#27015)

This commit is contained in:
Wesley Wigham 2018-09-17 15:19:11 -07:00 committed by GitHub
parent bc709a87ad
commit c3b4f72498
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 207 additions and 2 deletions

View file

@ -13373,7 +13373,7 @@ namespace ts {
let propagationType: Type;
inferFromTypes(originalSource, originalTarget);
function inferFromTypes(source: Type, target: Type) {
function inferFromTypes(source: Type, target: Type): void {
if (!couldContainTypeVariables(target)) {
return;
}
@ -13541,7 +13541,14 @@ namespace ts {
}
else {
if (!(priority & InferencePriority.NoConstraints && source.flags & (TypeFlags.Intersection | TypeFlags.Instantiable))) {
source = getApparentType(source);
const apparentSource = getApparentType(source);
// getApparentType can return _any_ type, since an indexed access or conditional may simplify to any other type.
// If that occurs and it doesn't simplify to an object or intersection, we'll need to restart `inferFromTypes`
// with the simplified source.
if (apparentSource !== source && !(apparentSource.flags & (TypeFlags.Object | TypeFlags.Intersection))) {
return inferFromTypes(apparentSource, target);
}
source = apparentSource;
}
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
const key = source.id + "," + target.id;

View file

@ -0,0 +1,45 @@
//// [voidReturnIndexUnionInference.ts]
// repro from https://github.com/Microsoft/TypeScript/issues/25274
export function safeInvoke<A1, R>(
func: ((arg1: A1) => R) | null | undefined,
arg1: A1
): R | undefined {
if (func) {
return func(arg1);
} else {
return undefined;
}
}
interface Props {
onFoo?(value: string): boolean;
onBar?(value: string): void;
}
function bad<P extends Props>(props: Readonly<P>) {
safeInvoke(props.onFoo, "blah");
// ERROR HERE!!!
// Type R in signature of safeInvoke incorrectly inferred as {} instead of void!
safeInvoke(props.onBar, "blah");
}
//// [voidReturnIndexUnionInference.js]
"use strict";
exports.__esModule = true;
// repro from https://github.com/Microsoft/TypeScript/issues/25274
function safeInvoke(func, arg1) {
if (func) {
return func(arg1);
}
else {
return undefined;
}
}
exports.safeInvoke = safeInvoke;
function bad(props) {
safeInvoke(props.onFoo, "blah");
// ERROR HERE!!!
// Type R in signature of safeInvoke incorrectly inferred as {} instead of void!
safeInvoke(props.onBar, "blah");
}

View file

@ -0,0 +1,68 @@
=== tests/cases/compiler/voidReturnIndexUnionInference.ts ===
// repro from https://github.com/Microsoft/TypeScript/issues/25274
export function safeInvoke<A1, R>(
>safeInvoke : Symbol(safeInvoke, Decl(voidReturnIndexUnionInference.ts, 0, 0))
>A1 : Symbol(A1, Decl(voidReturnIndexUnionInference.ts, 1, 27))
>R : Symbol(R, Decl(voidReturnIndexUnionInference.ts, 1, 30))
func: ((arg1: A1) => R) | null | undefined,
>func : Symbol(func, Decl(voidReturnIndexUnionInference.ts, 1, 34))
>arg1 : Symbol(arg1, Decl(voidReturnIndexUnionInference.ts, 2, 12))
>A1 : Symbol(A1, Decl(voidReturnIndexUnionInference.ts, 1, 27))
>R : Symbol(R, Decl(voidReturnIndexUnionInference.ts, 1, 30))
arg1: A1
>arg1 : Symbol(arg1, Decl(voidReturnIndexUnionInference.ts, 2, 47))
>A1 : Symbol(A1, Decl(voidReturnIndexUnionInference.ts, 1, 27))
): R | undefined {
>R : Symbol(R, Decl(voidReturnIndexUnionInference.ts, 1, 30))
if (func) {
>func : Symbol(func, Decl(voidReturnIndexUnionInference.ts, 1, 34))
return func(arg1);
>func : Symbol(func, Decl(voidReturnIndexUnionInference.ts, 1, 34))
>arg1 : Symbol(arg1, Decl(voidReturnIndexUnionInference.ts, 2, 47))
} else {
return undefined;
>undefined : Symbol(undefined)
}
}
interface Props {
>Props : Symbol(Props, Decl(voidReturnIndexUnionInference.ts, 10, 1))
onFoo?(value: string): boolean;
>onFoo : Symbol(Props.onFoo, Decl(voidReturnIndexUnionInference.ts, 12, 17))
>value : Symbol(value, Decl(voidReturnIndexUnionInference.ts, 13, 11))
onBar?(value: string): void;
>onBar : Symbol(Props.onBar, Decl(voidReturnIndexUnionInference.ts, 13, 35))
>value : Symbol(value, Decl(voidReturnIndexUnionInference.ts, 14, 11))
}
function bad<P extends Props>(props: Readonly<P>) {
>bad : Symbol(bad, Decl(voidReturnIndexUnionInference.ts, 15, 1))
>P : Symbol(P, Decl(voidReturnIndexUnionInference.ts, 17, 13))
>Props : Symbol(Props, Decl(voidReturnIndexUnionInference.ts, 10, 1))
>props : Symbol(props, Decl(voidReturnIndexUnionInference.ts, 17, 30))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>P : Symbol(P, Decl(voidReturnIndexUnionInference.ts, 17, 13))
safeInvoke(props.onFoo, "blah");
>safeInvoke : Symbol(safeInvoke, Decl(voidReturnIndexUnionInference.ts, 0, 0))
>props.onFoo : Symbol(onFoo, Decl(voidReturnIndexUnionInference.ts, 12, 17))
>props : Symbol(props, Decl(voidReturnIndexUnionInference.ts, 17, 30))
>onFoo : Symbol(onFoo, Decl(voidReturnIndexUnionInference.ts, 12, 17))
// ERROR HERE!!!
// Type R in signature of safeInvoke incorrectly inferred as {} instead of void!
safeInvoke(props.onBar, "blah");
>safeInvoke : Symbol(safeInvoke, Decl(voidReturnIndexUnionInference.ts, 0, 0))
>props.onBar : Symbol(onBar, Decl(voidReturnIndexUnionInference.ts, 13, 35))
>props : Symbol(props, Decl(voidReturnIndexUnionInference.ts, 17, 30))
>onBar : Symbol(onBar, Decl(voidReturnIndexUnionInference.ts, 13, 35))
}

View file

@ -0,0 +1,61 @@
=== tests/cases/compiler/voidReturnIndexUnionInference.ts ===
// repro from https://github.com/Microsoft/TypeScript/issues/25274
export function safeInvoke<A1, R>(
>safeInvoke : <A1, R>(func: ((arg1: A1) => R) | null | undefined, arg1: A1) => R | undefined
func: ((arg1: A1) => R) | null | undefined,
>func : ((arg1: A1) => R) | null | undefined
>arg1 : A1
>null : null
arg1: A1
>arg1 : A1
): R | undefined {
if (func) {
>func : ((arg1: A1) => R) | null | undefined
return func(arg1);
>func(arg1) : R
>func : (arg1: A1) => R
>arg1 : A1
} else {
return undefined;
>undefined : undefined
}
}
interface Props {
onFoo?(value: string): boolean;
>onFoo : ((value: string) => boolean) | undefined
>value : string
onBar?(value: string): void;
>onBar : ((value: string) => void) | undefined
>value : string
}
function bad<P extends Props>(props: Readonly<P>) {
>bad : <P extends Props>(props: Readonly<P>) => void
>props : Readonly<P>
safeInvoke(props.onFoo, "blah");
>safeInvoke(props.onFoo, "blah") : boolean | undefined
>safeInvoke : <A1, R>(func: ((arg1: A1) => R) | null | undefined, arg1: A1) => R | undefined
>props.onFoo : P["onFoo"]
>props : Readonly<P>
>onFoo : P["onFoo"]
>"blah" : "blah"
// ERROR HERE!!!
// Type R in signature of safeInvoke incorrectly inferred as {} instead of void!
safeInvoke(props.onBar, "blah");
>safeInvoke(props.onBar, "blah") : void | undefined
>safeInvoke : <A1, R>(func: ((arg1: A1) => R) | null | undefined, arg1: A1) => R | undefined
>props.onBar : P["onBar"]
>props : Readonly<P>
>onBar : P["onBar"]
>"blah" : "blah"
}

View file

@ -0,0 +1,24 @@
// @strict: true
// repro from https://github.com/Microsoft/TypeScript/issues/25274
export function safeInvoke<A1, R>(
func: ((arg1: A1) => R) | null | undefined,
arg1: A1
): R | undefined {
if (func) {
return func(arg1);
} else {
return undefined;
}
}
interface Props {
onFoo?(value: string): boolean;
onBar?(value: string): void;
}
function bad<P extends Props>(props: Readonly<P>) {
safeInvoke(props.onFoo, "blah");
// ERROR HERE!!!
// Type R in signature of safeInvoke incorrectly inferred as {} instead of void!
safeInvoke(props.onBar, "blah");
}