import { type ComputedRef, type MaybeRef, type Ref, computed, defineComponent, defineModel, reactive, ref, shallowRef, watch, } from 'vue' import { expectType } from './utils' const source = ref('foo') const source2 = computed(() => source.value) const source3 = () => 1 type Bar = Ref | ComputedRef | (() => number) type Foo = readonly [Ref, ComputedRef, () => number] type OnCleanup = (fn: () => void) => void const readonlyArr: Foo = [source, source2, source3] // lazy watcher will have consistent types for oldValue. watch(source, (value, oldValue, onCleanup) => { expectType(value) expectType(oldValue) expectType(onCleanup) }) watch([source, source2, source3], (values, oldValues) => { expectType<[string, string, number]>(values) expectType<[string, string, number]>(oldValues) }) // const array watch([source, source2, source3] as const, (values, oldValues) => { expectType>(values) expectType>(oldValues) }) // reactive array watch(reactive([source, source2, source3]), (value, oldValues) => { expectType(value) expectType(oldValues) }) // reactive w/ readonly tuple watch(reactive([source, source2, source3] as const), (value, oldValues) => { expectType(value) expectType(oldValues) }) // readonly array watch(readonlyArr, (values, oldValues) => { expectType>(values) expectType>(oldValues) }) // no type error, case from vueuse declare const aAny: any watch(aAny, (v, ov) => {}) watch(aAny, (v, ov) => {}, { immediate: true }) // immediate watcher's oldValue will be undefined on first run. watch( source, (value, oldValue) => { expectType(value) expectType(oldValue) }, { immediate: true }, ) watch( [source, source2, source3], (values, oldValues) => { expectType<[string, string, number]>(values) expectType<[string | undefined, string | undefined, number | undefined]>( oldValues, ) }, { immediate: true }, ) // const array watch( [source, source2, source3] as const, (values, oldValues) => { expectType>(values) expectType< Readonly<[string | undefined, string | undefined, number | undefined]> >(oldValues) }, { immediate: true }, ) // reactive array watch( reactive([source, source2, source3]), (value, oldVals) => { expectType(value) expectType(oldVals) }, { immediate: true }, ) // reactive w/ readonly tuple watch(reactive([source, source2, source3] as const), (value, oldVals) => { expectType(value) expectType(oldVals) }) // readonly array watch( readonlyArr, (values, oldValues) => { expectType>(values) expectType< Readonly<[string | undefined, string | undefined, number | undefined]> >(oldValues) }, { immediate: true }, ) // should provide correct ref.value inner type to callbacks const nestedRefSource = ref({ foo: ref(1), }) watch(nestedRefSource, (v, ov) => { expectType<{ foo: number }>(v) expectType<{ foo: number }>(ov) }) const someRef = ref({ test: 'test' }) const otherRef = ref({ a: 'b' }) watch([someRef, otherRef], values => { const value1 = values[0] // no type error console.log(value1.test) const value2 = values[1] // no type error console.log(value2.a) }) // #6135 defineComponent({ data() { return { a: 1 } }, created() { this.$watch( () => this.a, (v, ov, onCleanup) => { expectType(v) expectType(ov) expectType(onCleanup) }, ) }, }) { //#7852 type Steps = { step: '1' } | { step: '2' } const shallowUnionGenParam = shallowRef({ step: '1' }) const shallowUnionAsCast = shallowRef({ step: '1' } as Steps) watch(shallowUnionGenParam, value => { expectType(value) }) watch(shallowUnionAsCast, value => { expectType(value) }) } { // defineModel const bool = defineModel({ default: false }) watch(bool, value => { expectType(value) }) const bool1 = defineModel() watch(bool1, value => { expectType(value) }) const msg = defineModel({ required: true }) watch(msg, value => { expectType(value) }) const arr = defineModel({ required: true }) watch(arr, value => { expectType(value) }) const obj = defineModel<{ foo: string }>({ required: true }) watch(obj, value => { expectType<{ foo: string }>(value) }) } { const css: MaybeRef = '' watch(ref(css), value => { expectType(value) }) }