Skip to content

Commit

Permalink
Cherry-pick PR microsoft#33223 into release-3.6
Browse files Browse the repository at this point in the history
Component commits:
a2b3af2 Elevate mapped types over type parameters in the same way as naked type parameters in intersection inference prioritization
  • Loading branch information
weswigham authored and typescript-bot committed Sep 4, 2019
1 parent 1ec17a8 commit cbe2fdd
Show file tree
Hide file tree
Showing 9 changed files with 680 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/compiler/checker.ts
Expand Up @@ -15538,11 +15538,14 @@ namespace ts {
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy)) return;
}
}
else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types, t => !!getInferenceInfoForType(t))) {
else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types,
t => !!getInferenceInfoForType(t) || (isGenericMappedType(t) && !!getInferenceInfoForType(getHomomorphicTypeVariable(t) || neverType)))) {
// We reduce intersection types only when they contain naked type parameters. For example, when
// inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and
// infer { extra: any } for T. But when inferring to 'string[] & Iterable<T>' we want to keep the
// string[] on the source side and infer string for T.
// Likewise, we consider a homomorphic mapped type constrainted to the target type parameter as similar to a "naked type variable"
// in such scenarios.
if (source.flags & TypeFlags.Intersection) {
// Infer between identically matching source and target constituents and remove the matching types.
const [sources, targets] = inferFromMatchingTypes((<IntersectionType>source).types, (<IntersectionType>target).types, isTypeIdenticalTo);
Expand Down
63 changes: 63 additions & 0 deletions tests/baselines/reference/vueLikeDataAndPropsInference.js
@@ -0,0 +1,63 @@
//// [vueLikeDataAndPropsInference.ts]
interface Instance {
_instanceBrand: never
}

type DataDef<Data, Props> = (this: Readonly<Props> & Instance) => Data

type PropsDefinition<T> = {
[K in keyof T]: T[K]
}

interface Options<
Data = ((this: Instance) => object),
PropsDef = {}
> {
data?: Data
props?: PropsDef
watch?: Record<string, WatchHandler<any>>
}

type WatchHandler<T> = (val: T, oldVal: T) => void;

type ThisTypedOptions<Data, Props> =
Options<DataDef<Data, Props>, PropsDefinition<Props>> &
ThisType<Data & Readonly<Props> & Instance>

declare function test<Data, Props>(fn: ThisTypedOptions<Data, Props>): void;
declare function test(fn: Options): void;

test({
props: {
foo: ''
},

data(): { bar: boolean } {
return {
bar: true
}
},

watch: {
foo(newVal: string, oldVal: string): void {
this.bar = false
}
}
})

//// [vueLikeDataAndPropsInference.js]
test({
props: {
foo: ''
},
data: function () {
return {
bar: true
};
},
watch: {
foo: function (newVal, oldVal) {
this.bar = false;
}
}
});
130 changes: 130 additions & 0 deletions tests/baselines/reference/vueLikeDataAndPropsInference.symbols
@@ -0,0 +1,130 @@
=== tests/cases/compiler/vueLikeDataAndPropsInference.ts ===
interface Instance {
>Instance : Symbol(Instance, Decl(vueLikeDataAndPropsInference.ts, 0, 0))

_instanceBrand: never
>_instanceBrand : Symbol(Instance._instanceBrand, Decl(vueLikeDataAndPropsInference.ts, 0, 20))
}

type DataDef<Data, Props> = (this: Readonly<Props> & Instance) => Data
>DataDef : Symbol(DataDef, Decl(vueLikeDataAndPropsInference.ts, 2, 1))
>Data : Symbol(Data, Decl(vueLikeDataAndPropsInference.ts, 4, 13))
>Props : Symbol(Props, Decl(vueLikeDataAndPropsInference.ts, 4, 18))
>this : Symbol(this, Decl(vueLikeDataAndPropsInference.ts, 4, 29))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>Props : Symbol(Props, Decl(vueLikeDataAndPropsInference.ts, 4, 18))
>Instance : Symbol(Instance, Decl(vueLikeDataAndPropsInference.ts, 0, 0))
>Data : Symbol(Data, Decl(vueLikeDataAndPropsInference.ts, 4, 13))

type PropsDefinition<T> = {
>PropsDefinition : Symbol(PropsDefinition, Decl(vueLikeDataAndPropsInference.ts, 4, 70))
>T : Symbol(T, Decl(vueLikeDataAndPropsInference.ts, 6, 21))

[K in keyof T]: T[K]
>K : Symbol(K, Decl(vueLikeDataAndPropsInference.ts, 7, 5))
>T : Symbol(T, Decl(vueLikeDataAndPropsInference.ts, 6, 21))
>T : Symbol(T, Decl(vueLikeDataAndPropsInference.ts, 6, 21))
>K : Symbol(K, Decl(vueLikeDataAndPropsInference.ts, 7, 5))
}

interface Options<
>Options : Symbol(Options, Decl(vueLikeDataAndPropsInference.ts, 8, 1))

Data = ((this: Instance) => object),
>Data : Symbol(Data, Decl(vueLikeDataAndPropsInference.ts, 10, 18))
>this : Symbol(this, Decl(vueLikeDataAndPropsInference.ts, 11, 13))
>Instance : Symbol(Instance, Decl(vueLikeDataAndPropsInference.ts, 0, 0))

PropsDef = {}
>PropsDef : Symbol(PropsDef, Decl(vueLikeDataAndPropsInference.ts, 11, 40))

> {
data?: Data
>data : Symbol(Options.data, Decl(vueLikeDataAndPropsInference.ts, 13, 7))
>Data : Symbol(Data, Decl(vueLikeDataAndPropsInference.ts, 10, 18))

props?: PropsDef
>props : Symbol(Options.props, Decl(vueLikeDataAndPropsInference.ts, 14, 15))
>PropsDef : Symbol(PropsDef, Decl(vueLikeDataAndPropsInference.ts, 11, 40))

watch?: Record<string, WatchHandler<any>>
>watch : Symbol(Options.watch, Decl(vueLikeDataAndPropsInference.ts, 15, 20))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>WatchHandler : Symbol(WatchHandler, Decl(vueLikeDataAndPropsInference.ts, 17, 1))
}

type WatchHandler<T> = (val: T, oldVal: T) => void;
>WatchHandler : Symbol(WatchHandler, Decl(vueLikeDataAndPropsInference.ts, 17, 1))
>T : Symbol(T, Decl(vueLikeDataAndPropsInference.ts, 19, 18))
>val : Symbol(val, Decl(vueLikeDataAndPropsInference.ts, 19, 24))
>T : Symbol(T, Decl(vueLikeDataAndPropsInference.ts, 19, 18))
>oldVal : Symbol(oldVal, Decl(vueLikeDataAndPropsInference.ts, 19, 31))
>T : Symbol(T, Decl(vueLikeDataAndPropsInference.ts, 19, 18))

type ThisTypedOptions<Data, Props> =
>ThisTypedOptions : Symbol(ThisTypedOptions, Decl(vueLikeDataAndPropsInference.ts, 19, 51))
>Data : Symbol(Data, Decl(vueLikeDataAndPropsInference.ts, 21, 22))
>Props : Symbol(Props, Decl(vueLikeDataAndPropsInference.ts, 21, 27))

Options<DataDef<Data, Props>, PropsDefinition<Props>> &
>Options : Symbol(Options, Decl(vueLikeDataAndPropsInference.ts, 8, 1))
>DataDef : Symbol(DataDef, Decl(vueLikeDataAndPropsInference.ts, 2, 1))
>Data : Symbol(Data, Decl(vueLikeDataAndPropsInference.ts, 21, 22))
>Props : Symbol(Props, Decl(vueLikeDataAndPropsInference.ts, 21, 27))
>PropsDefinition : Symbol(PropsDefinition, Decl(vueLikeDataAndPropsInference.ts, 4, 70))
>Props : Symbol(Props, Decl(vueLikeDataAndPropsInference.ts, 21, 27))

ThisType<Data & Readonly<Props> & Instance>
>ThisType : Symbol(ThisType, Decl(lib.es5.d.ts, --, --))
>Data : Symbol(Data, Decl(vueLikeDataAndPropsInference.ts, 21, 22))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>Props : Symbol(Props, Decl(vueLikeDataAndPropsInference.ts, 21, 27))
>Instance : Symbol(Instance, Decl(vueLikeDataAndPropsInference.ts, 0, 0))

declare function test<Data, Props>(fn: ThisTypedOptions<Data, Props>): void;
>test : Symbol(test, Decl(vueLikeDataAndPropsInference.ts, 23, 47), Decl(vueLikeDataAndPropsInference.ts, 25, 76))
>Data : Symbol(Data, Decl(vueLikeDataAndPropsInference.ts, 25, 22))
>Props : Symbol(Props, Decl(vueLikeDataAndPropsInference.ts, 25, 27))
>fn : Symbol(fn, Decl(vueLikeDataAndPropsInference.ts, 25, 35))
>ThisTypedOptions : Symbol(ThisTypedOptions, Decl(vueLikeDataAndPropsInference.ts, 19, 51))
>Data : Symbol(Data, Decl(vueLikeDataAndPropsInference.ts, 25, 22))
>Props : Symbol(Props, Decl(vueLikeDataAndPropsInference.ts, 25, 27))

declare function test(fn: Options): void;
>test : Symbol(test, Decl(vueLikeDataAndPropsInference.ts, 23, 47), Decl(vueLikeDataAndPropsInference.ts, 25, 76))
>fn : Symbol(fn, Decl(vueLikeDataAndPropsInference.ts, 26, 22))
>Options : Symbol(Options, Decl(vueLikeDataAndPropsInference.ts, 8, 1))

test({
>test : Symbol(test, Decl(vueLikeDataAndPropsInference.ts, 23, 47), Decl(vueLikeDataAndPropsInference.ts, 25, 76))

props: {
>props : Symbol(props, Decl(vueLikeDataAndPropsInference.ts, 28, 6))

foo: ''
>foo : Symbol(foo, Decl(vueLikeDataAndPropsInference.ts, 29, 12))

},

data(): { bar: boolean } {
>data : Symbol(data, Decl(vueLikeDataAndPropsInference.ts, 31, 6))
>bar : Symbol(bar, Decl(vueLikeDataAndPropsInference.ts, 33, 13))

return {
bar: true
>bar : Symbol(bar, Decl(vueLikeDataAndPropsInference.ts, 34, 16))
}
},

watch: {
>watch : Symbol(watch, Decl(vueLikeDataAndPropsInference.ts, 37, 6))

foo(newVal: string, oldVal: string): void {
>foo : Symbol(foo, Decl(vueLikeDataAndPropsInference.ts, 39, 12))
>newVal : Symbol(newVal, Decl(vueLikeDataAndPropsInference.ts, 40, 12))
>oldVal : Symbol(oldVal, Decl(vueLikeDataAndPropsInference.ts, 40, 27))

this.bar = false
}
}
})
97 changes: 97 additions & 0 deletions tests/baselines/reference/vueLikeDataAndPropsInference.types
@@ -0,0 +1,97 @@
=== tests/cases/compiler/vueLikeDataAndPropsInference.ts ===
interface Instance {
_instanceBrand: never
>_instanceBrand : never
}

type DataDef<Data, Props> = (this: Readonly<Props> & Instance) => Data
>DataDef : DataDef<Data, Props>
>this : Readonly<Props> & Instance

type PropsDefinition<T> = {
>PropsDefinition : PropsDefinition<T>

[K in keyof T]: T[K]
}

interface Options<
Data = ((this: Instance) => object),
>this : Instance

PropsDef = {}
> {
data?: Data
>data : Data

props?: PropsDef
>props : PropsDef

watch?: Record<string, WatchHandler<any>>
>watch : Record<string, WatchHandler<any>>
}

type WatchHandler<T> = (val: T, oldVal: T) => void;
>WatchHandler : WatchHandler<T>
>val : T
>oldVal : T

type ThisTypedOptions<Data, Props> =
>ThisTypedOptions : ThisTypedOptions<Data, Props>

Options<DataDef<Data, Props>, PropsDefinition<Props>> &
ThisType<Data & Readonly<Props> & Instance>

declare function test<Data, Props>(fn: ThisTypedOptions<Data, Props>): void;
>test : { <Data, Props>(fn: ThisTypedOptions<Data, Props>): void; (fn: Options<(this: Instance) => object, {}>): void; }
>fn : ThisTypedOptions<Data, Props>

declare function test(fn: Options): void;
>test : { <Data, Props>(fn: ThisTypedOptions<Data, Props>): void; (fn: Options<(this: Instance) => object, {}>): void; }
>fn : Options<(this: Instance) => object, {}>

test({
>test({ props: { foo: '' }, data(): { bar: boolean } { return { bar: true } }, watch: { foo(newVal: string, oldVal: string): void { this.bar = false } }}) : void
>test : { <Data, Props>(fn: ThisTypedOptions<Data, Props>): void; (fn: Options<(this: Instance) => object, {}>): void; }
>{ props: { foo: '' }, data(): { bar: boolean } { return { bar: true } }, watch: { foo(newVal: string, oldVal: string): void { this.bar = false } }} : { props: { foo: string; }; data(this: Readonly<{ foo: string; }> & Instance): { bar: boolean; }; watch: { foo(newVal: string, oldVal: string): void; }; }

props: {
>props : { foo: string; }
>{ foo: '' } : { foo: string; }

foo: ''
>foo : string
>'' : ""

},

data(): { bar: boolean } {
>data : (this: Readonly<{ foo: string; }> & Instance) => { bar: boolean; }
>bar : boolean

return {
>{ bar: true } : { bar: true; }

bar: true
>bar : true
>true : true
}
},

watch: {
>watch : { foo(newVal: string, oldVal: string): void; }
>{ foo(newVal: string, oldVal: string): void { this.bar = false } } : { foo(newVal: string, oldVal: string): void; }

foo(newVal: string, oldVal: string): void {
>foo : (newVal: string, oldVal: string) => void
>newVal : string
>oldVal : string

this.bar = false
>this.bar = false : false
>this.bar : any
>this : any
>bar : any
>false : false
}
}
})
64 changes: 64 additions & 0 deletions tests/baselines/reference/vueLikeDataAndPropsInference2.js
@@ -0,0 +1,64 @@
//// [vueLikeDataAndPropsInference2.ts]
interface Instance {
_instanceBrand: never
}

type DataDef<Data, Props> = (this: Readonly<Props> & Instance) => Data

type PropsDefinition<T> = {
[K in keyof T]: T[K]
}

interface Options<
Data = object | ((this: Instance) => object),
PropsDef = PropsDefinition<Record<string, any>>
> {
data?: Data
props?: PropsDef
watch?: Record<string, WatchHandler<any>>
}

type WatchHandler<T> = (val: T, oldVal: T) => void;

type ThisTypedOptions<Data, Props> =
object &
Options<DataDef<Data, Props>, PropsDefinition<Props>> &
ThisType<Data & Readonly<Props> & Instance>

declare function test<Data, Props>(fn: ThisTypedOptions<Data, Props>): void;
declare function test(fn: Options): void;

test({
props: {
foo: ''
},

data(): { bar: boolean } {
return {
bar: true
}
},

watch: {
foo(newVal: string, oldVal: string): void {
this.bar = false
}
}
})

//// [vueLikeDataAndPropsInference2.js]
test({
props: {
foo: ''
},
data: function () {
return {
bar: true
};
},
watch: {
foo: function (newVal, oldVal) {
this.bar = false;
}
}
});

0 comments on commit cbe2fdd

Please sign in to comment.