From 9554f09d0924c477f120fd331b4f3b37551c3345 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 17 Oct 2018 14:44:39 -0700 Subject: [PATCH] Add ability to infer to the simplified form of a type variable (#27953) * Add ability to infer to the simplified form of a type variable * Add test --- src/compiler/checker.ts | 11 +++ ...edIndexesOfIntersectionsAreInferencable.js | 33 +++++++++ ...exesOfIntersectionsAreInferencable.symbols | 74 +++++++++++++++++++ ...ndexesOfIntersectionsAreInferencable.types | 49 ++++++++++++ ...edIndexesOfIntersectionsAreInferencable.ts | 22 ++++++ 5 files changed, 189 insertions(+) create mode 100644 tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.js create mode 100644 tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.symbols create mode 100644 tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.types create mode 100644 tests/cases/compiler/complicatedIndexesOfIntersectionsAreInferencable.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 604805545a4..b8f20f5b9ad 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13644,6 +13644,17 @@ namespace ts { } return; } + else { + // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine + const simplified = getSimplifiedType(target); + if (simplified !== target) { + const key = source.id + "," + simplified.id; + if (!visited || !visited.get(key)) { + (visited || (visited = createMap())).set(key, true); + inferFromTypes(source, simplified); + } + } + } } if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // If source and target are references to the same generic type, infer from type arguments diff --git a/tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.js b/tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.js new file mode 100644 index 00000000000..46e537bc64a --- /dev/null +++ b/tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.js @@ -0,0 +1,33 @@ +//// [complicatedIndexesOfIntersectionsAreInferencable.ts] +interface FormikConfig { + initialValues: Values; + validate?: (props: Values) => void; + validateOnChange?: boolean; +} + +declare function Func( + x: (string extends "validate" | "initialValues" | keyof ExtraProps + ? Readonly & ExtraProps> + : Pick & ExtraProps>, "validate" | "initialValues" | Exclude> + & Partial & ExtraProps>, "validateOnChange" | Extract>>) +): void; + +Func({ + initialValues: { + foo: "" + }, + validate: props => { + props.foo; + } +}); + +//// [complicatedIndexesOfIntersectionsAreInferencable.js] +"use strict"; +Func({ + initialValues: { + foo: "" + }, + validate: function (props) { + props.foo; + } +}); diff --git a/tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.symbols b/tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.symbols new file mode 100644 index 00000000000..d6ad3f1c3cd --- /dev/null +++ b/tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.symbols @@ -0,0 +1,74 @@ +=== tests/cases/compiler/complicatedIndexesOfIntersectionsAreInferencable.ts === +interface FormikConfig { +>FormikConfig : Symbol(FormikConfig, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 0, 0)) +>Values : Symbol(Values, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 0, 23)) + + initialValues: Values; +>initialValues : Symbol(FormikConfig.initialValues, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 0, 32)) +>Values : Symbol(Values, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 0, 23)) + + validate?: (props: Values) => void; +>validate : Symbol(FormikConfig.validate, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 1, 26)) +>props : Symbol(props, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 2, 16)) +>Values : Symbol(Values, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 0, 23)) + + validateOnChange?: boolean; +>validateOnChange : Symbol(FormikConfig.validateOnChange, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 2, 39)) +} + +declare function Func( +>Func : Symbol(Func, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 4, 1)) +>Values : Symbol(Values, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 22)) +>ExtraProps : Symbol(ExtraProps, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 38)) + + x: (string extends "validate" | "initialValues" | keyof ExtraProps +>x : Symbol(x, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 56)) +>ExtraProps : Symbol(ExtraProps, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 38)) + + ? Readonly & ExtraProps> +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>FormikConfig : Symbol(FormikConfig, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 0, 0)) +>Values : Symbol(Values, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 22)) +>ExtraProps : Symbol(ExtraProps, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 38)) + + : Pick & ExtraProps>, "validate" | "initialValues" | Exclude> +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>FormikConfig : Symbol(FormikConfig, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 0, 0)) +>Values : Symbol(Values, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 22)) +>ExtraProps : Symbol(ExtraProps, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 38)) +>Exclude : Symbol(Exclude, Decl(lib.es5.d.ts, --, --)) +>ExtraProps : Symbol(ExtraProps, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 38)) + + & Partial & ExtraProps>, "validateOnChange" | Extract>>) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --)) +>FormikConfig : Symbol(FormikConfig, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 0, 0)) +>Values : Symbol(Values, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 22)) +>ExtraProps : Symbol(ExtraProps, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 38)) +>Extract : Symbol(Extract, Decl(lib.es5.d.ts, --, --)) +>ExtraProps : Symbol(ExtraProps, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 6, 38)) + +): void; + +Func({ +>Func : Symbol(Func, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 4, 1)) + + initialValues: { +>initialValues : Symbol(initialValues, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 13, 6)) + + foo: "" +>foo : Symbol(foo, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 14, 20)) + + }, + validate: props => { +>validate : Symbol(validate, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 16, 6)) +>props : Symbol(props, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 17, 13)) + + props.foo; +>props.foo : Symbol(foo, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 14, 20)) +>props : Symbol(props, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 17, 13)) +>foo : Symbol(foo, Decl(complicatedIndexesOfIntersectionsAreInferencable.ts, 14, 20)) + } +}); diff --git a/tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.types b/tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.types new file mode 100644 index 00000000000..64511cb9df2 --- /dev/null +++ b/tests/baselines/reference/complicatedIndexesOfIntersectionsAreInferencable.types @@ -0,0 +1,49 @@ +=== tests/cases/compiler/complicatedIndexesOfIntersectionsAreInferencable.ts === +interface FormikConfig { + initialValues: Values; +>initialValues : Values + + validate?: (props: Values) => void; +>validate : ((props: Values) => void) | undefined +>props : Values + + validateOnChange?: boolean; +>validateOnChange : boolean | undefined +} + +declare function Func( +>Func : (x: string extends "validate" | "initialValues" | keyof ExtraProps ? Readonly & ExtraProps> : Pick & ExtraProps>, "validate" | "initialValues" | Exclude> & Partial & ExtraProps>, "validateOnChange" | Extract>>) => void + + x: (string extends "validate" | "initialValues" | keyof ExtraProps +>x : string extends "validate" | "initialValues" | keyof ExtraProps ? Readonly & ExtraProps> : Pick & ExtraProps>, "validate" | "initialValues" | Exclude> & Partial & ExtraProps>, "validateOnChange" | Extract>> + + ? Readonly & ExtraProps> + : Pick & ExtraProps>, "validate" | "initialValues" | Exclude> + & Partial & ExtraProps>, "validateOnChange" | Extract>>) +): void; + +Func({ +>Func({ initialValues: { foo: "" }, validate: props => { props.foo; }}) : void +>Func : (x: string extends "validate" | "initialValues" | keyof ExtraProps ? Readonly & ExtraProps> : Pick & ExtraProps>, "validate" | "initialValues" | Exclude> & Partial & ExtraProps>, "validateOnChange" | Extract>>) => void +>{ initialValues: { foo: "" }, validate: props => { props.foo; }} : { initialValues: { foo: string; }; validate: (props: { foo: string; }) => void; } + + initialValues: { +>initialValues : { foo: string; } +>{ foo: "" } : { foo: string; } + + foo: "" +>foo : string +>"" : "" + + }, + validate: props => { +>validate : (props: { foo: string; }) => void +>props => { props.foo; } : (props: { foo: string; }) => void +>props : { foo: string; } + + props.foo; +>props.foo : string +>props : { foo: string; } +>foo : string + } +}); diff --git a/tests/cases/compiler/complicatedIndexesOfIntersectionsAreInferencable.ts b/tests/cases/compiler/complicatedIndexesOfIntersectionsAreInferencable.ts new file mode 100644 index 00000000000..650d8886cae --- /dev/null +++ b/tests/cases/compiler/complicatedIndexesOfIntersectionsAreInferencable.ts @@ -0,0 +1,22 @@ +// @strict: true +interface FormikConfig { + initialValues: Values; + validate?: (props: Values) => void; + validateOnChange?: boolean; +} + +declare function Func( + x: (string extends "validate" | "initialValues" | keyof ExtraProps + ? Readonly & ExtraProps> + : Pick & ExtraProps>, "validate" | "initialValues" | Exclude> + & Partial & ExtraProps>, "validateOnChange" | Extract>>) +): void; + +Func({ + initialValues: { + foo: "" + }, + validate: props => { + props.foo; + } +}); \ No newline at end of file