Closed
Description
TypeScript Version: 3.6.1-rc, 3.6.2
Search Terms:
mapped types, argument, overload, readonly, intersection
Code
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
}
}
})
Expected behavior:
No errors. test
should choose the first overload.
Actual behavior:
There is an error because the test
choose the fallback overload and this
is not typed as expected.
Type 'false' is not assignable to type 'WatchHandler<any>'.
If you change the fallback overload's argument type to never
, you can see that it wrongly infers Props
type parameter in error message.
This will not happen if you do either following things:
- Replace all appearance of
PropsDefinition<XXX>
with justXXX
(PropsDefinition
is just returning the same type but using mapped type) - Replace the
this
type ofDataDef
(Readonly<Props> & Instance
) withProps & Instance
Related Issues:
This is originally reported on Vue.js repo.
vuejs/vue#10455