vue/types/test/options-test.ts
2022-05-27 17:26:55 +08:00

510 lines
9.9 KiB
TypeScript

import Vue, { PropType, VNode } from '../index'
import { ComponentOptions, Component } from '../index'
import { CreateElement } from '../vue'
interface MyComponent extends Vue {
a: number
}
const option: ComponentOptions<MyComponent> = {
data() {
return {
a: 123
}
}
}
// contravariant generic should use never
const anotherOption: ComponentOptions<never> = option
const componentType: Component = option
Vue.component('sub-component', {
components: {
a: Vue.component(''),
b: {}
}
})
Vue.component('prop-component', {
props: {
size: Number,
name: {
type: String,
default: '0',
required: true
}
},
data() {
return {
fixedSize: this.size.toFixed(),
capName: this.name.toUpperCase()
}
}
})
Vue.component('string-prop', {
props: ['size', 'name'],
data() {
return {
fixedSize: this.size.whatever,
capName: this.name.isany
}
}
})
class User {
private u = 1
}
class Cat {
private u = 1
}
interface IUser {
foo: string
bar: number
}
interface ICat {
foo: any
bar: object
}
type ConfirmCallback = (confirm: boolean) => void
Vue.component('union-prop', {
props: {
cat: Object as PropType<ICat>,
complexUnion: { type: [User, Number] as PropType<User | number> },
kittyUser: Object as PropType<ICat & IUser>,
callback: Function as PropType<ConfirmCallback>,
union: [User, Number] as PropType<User | number>
},
data() {
this.cat
this.complexUnion
this.kittyUser
this.callback(true)
this.union
return {
fixedSize: this.union
}
}
})
Vue.component('union-prop-with-no-casting', {
props: {
mixed: [RegExp, Array],
object: [Cat, User],
primitive: [String, Number],
regex: RegExp
},
data() {
this.mixed
this.object
this.primitive
this.regex.compile
}
})
Vue.component('prop-with-primitive-default', {
props: {
id: {
type: String,
default: () => String(Math.round(Math.random() * 10000000))
}
},
created(): void {
this.id
}
})
Vue.component('component', {
data() {
this.$mount
this.size
return {
a: 1
}
},
props: {
size: Number,
name: {
type: String,
default: '0',
required: true
}
},
propsData: {
msg: 'Hello'
},
computed: {
aDouble(): number {
return this.a * 2
},
aPlus: {
get(): number {
return this.a + 1
},
set(v: number) {
this.a = v - 1
},
cache: false
}
},
methods: {
plus(): void {
this.a++
this.aDouble.toFixed()
this.aPlus = 1
this.size.toFixed()
}
},
watch: {
a: function (val: number, oldVal: number) {
console.log(`new: ${val}, old: ${oldVal}`)
},
b: 'someMethod',
c: {
handler(val, oldVal) {
this.a = val
},
deep: true
},
d: {
handler: 'someMethod',
immediate: true
}
},
el: '#app',
template: '<div>{{ message }}</div>',
render(createElement) {
return createElement(
'div',
{
attrs: {
id: 'foo'
},
props: {
myProp: 'bar'
},
directives: [
{
name: 'a',
value: 'foo'
}
],
domProps: {
innerHTML: 'baz'
},
on: {
click: new Function()
},
nativeOn: {
click: new Function()
},
class: {
foo: true,
bar: false
},
style: {
color: 'red',
fontSize: '14px'
},
key: 'myKey',
ref: 'myRef',
refInFor: true
},
[
createElement(),
createElement('div', 'message'),
createElement(Vue.component('component')),
createElement({} as ComponentOptions<Vue>),
createElement({
functional: true,
render(c: CreateElement) {
return createElement()
}
}),
createElement(() => Vue.component('component')),
createElement(() => ({} as ComponentOptions<Vue>)),
createElement((resolve, reject) => {
resolve({} as ComponentOptions<Vue>)
reject()
}),
'message',
[createElement('div', 'message')]
]
)
},
renderError(createElement, err) {
return createElement('pre', { style: { color: 'red' } }, err.stack)
},
staticRenderFns: [],
beforeCreate() {
;(this as any).a = 1
},
created() {},
beforeDestroy() {},
destroyed() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
activated() {},
deactivated() {},
errorCaptured(err, vm, info) {
err.message
vm.$emit('error')
info.toUpperCase()
return true
},
serverPrefetch() {
return Promise.resolve()
},
directives: {
a: {
bind() {},
inserted() {},
update() {},
componentUpdated() {},
unbind() {}
},
b(el, binding, vnode, oldVnode) {
el.textContent
binding.name
binding.value
binding.oldValue
binding.expression
binding.arg
binding.modifiers['modifier']
}
},
components: {
a: Vue.component(''),
b: {} as ComponentOptions<Vue>
},
transitions: {},
filters: {
double(value: number) {
return value * 2
}
},
parent: new Vue(),
mixins: [Vue.component(''), {} as ComponentOptions<Vue>],
name: 'Component',
extends: {} as ComponentOptions<Vue>,
delimiters: ['${', '}']
})
Vue.component('custom-prop-type-function', {
props: {
callback: Function as PropType<(confirm: boolean) => void>
},
methods: {
confirm() {
this.callback(true)
}
}
})
Vue.component('provide-inject', {
provide: {
foo: 1
},
inject: {
injectFoo: 'foo',
injectBar: Symbol(),
injectBaz: { from: 'baz' },
injectQux: { default: 1 },
injectQuux: { from: 'quuz', default: () => ({ value: 1 }) }
}
})
Vue.component('provide-function', {
provide: () => ({
foo: 1
})
})
Vue.component('component-with-slot', {
render(h): VNode {
return h('div', this.$slots.default)
}
})
Vue.component('component-with-scoped-slot', {
render(h) {
interface ScopedSlotProps {
msg: string
}
return h('div', [
h('child', [
// default scoped slot as children
(props: ScopedSlotProps) => [h('span', [props.msg])]
]),
h('child', {
scopedSlots: {
// named scoped slot as vnode data
item: (props: ScopedSlotProps) => [h('span', [props.msg])]
}
}),
h('child', [
// return single VNode (will be normalized to an array)
(props: ScopedSlotProps) => h('span', [props.msg])
]),
h('child', {
// Passing down all slots from parent
scopedSlots: this.$scopedSlots
}),
h('child', {
// Passing down single slot from parent
scopedSlots: {
default: this.$scopedSlots.default
}
})
])
},
components: {
child: {
render(this: Vue, h: CreateElement) {
const defaultSlot = this.$scopedSlots['default']!({ msg: 'hi' })
defaultSlot &&
defaultSlot.forEach(vnode => {
vnode.tag
})
return h('div', [
defaultSlot,
this.$scopedSlots['item']!({ msg: 'hello' })
])
}
}
}
})
Vue.component('narrow-array-of-vnode-type', {
render(h): VNode {
const slot = this.$scopedSlots.default!({})
if (typeof slot === 'string') {
// <template slot-scope="data">bare string</template>
return h('span', slot)
} else if (Array.isArray(slot)) {
// template with multiple children
const first = slot[0]
if (!Array.isArray(first) && typeof first !== 'string' && first) {
return first
} else {
return h()
}
} else if (slot) {
// <div slot-scope="data">bare VNode</div>
return slot
} else {
// empty template, slot === undefined
return h()
}
}
})
Vue.component('functional-component', {
props: ['prop'],
functional: true,
inject: ['foo'],
render(createElement, context) {
context.props
context.children
context.slots()
context.data
context.parent
context.scopedSlots
context.listeners.click
return createElement('div', {}, context.children)
}
})
Vue.component('functional-component-object-inject', {
functional: true,
inject: {
foo: 'foo',
bar: Symbol(),
baz: { from: 'baz' },
qux: { default: 1 },
quux: { from: 'quuz', default: () => ({ value: 1 }) }
},
render(h) {
return h('div')
}
})
Vue.component('functional-component-check-optional', {
functional: true
})
Vue.component('functional-component-multi-root', {
functional: true,
render(h) {
return [
h('tr', [h('td', 'foo'), h('td', 'bar')]),
h('tr', [h('td', 'lorem'), h('td', 'ipsum')])
]
}
})
Vue.component('async-component', (resolve, reject) => {
setTimeout(() => {
resolve(Vue.component('component'))
}, 0)
return new Promise(resolve => {
resolve({
functional: true,
render(h: CreateElement) {
return h('div')
}
})
})
})
Vue.component('functional-component-v-model', {
props: ['foo'],
functional: true,
model: {
prop: 'foo',
event: 'change'
},
render(createElement, context) {
return createElement('input', {
on: {
input: new Function()
},
domProps: {
value: context.props.foo
}
})
}
})
Vue.component('async-es-module-component', () => import('./es-module'))
Vue.component('directive-expression-optional-string', {
render(createElement) {
return createElement('div', {
directives: [
{
name: 'has-expression',
value: 2,
expression: '1 + 1'
},
{
name: 'no-expression',
value: 'foo'
}
]
})
}
})