├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── babel.config.js ├── enable-check.d.ts ├── enable-check.js ├── jest.config.js ├── options ├── allow-element-unknown-attrs.d.ts ├── allow-props-object.d.ts ├── allow-unknown-props.d.ts ├── enable-html-attrs.d.ts ├── enable-nativeon.d.ts └── enable-vue-router.d.ts ├── package.json ├── src ├── advance.ts ├── api.ts ├── index.ts ├── modifiers.ts └── vca.ts ├── test ├── jest │ ├── classComponent.tsx │ ├── componentFactory.tsx │ ├── modifiers.tsx │ └── tsconfig.json └── tsc │ ├── allow-element-unknown-attrs │ ├── test.tsx │ └── tsconfig.json │ ├── allow-unknown-props │ ├── test.tsx │ └── tsconfig.json │ ├── base.json │ ├── basic │ ├── builtin-components.tsx │ ├── classComponent.tsx │ ├── componentFactory.tsx │ ├── extend.tsx │ ├── mixin.tsx │ ├── modifiers.tsx │ ├── propsObject.tsx │ ├── register-global-component.ts │ ├── test.tsx │ ├── tsconfig.json │ └── vca.tsx │ ├── declaration │ ├── test.tsx │ └── tsconfig.json │ ├── enable-html-attrs │ ├── test.tsx │ └── tsconfig.json │ ├── enable-nativeon │ ├── test.tsx │ └── tsconfig.json │ ├── enable-vuerouter │ ├── test.tsx │ └── tsconfig.json │ └── runner.js ├── tsconfig.json ├── types ├── base.d.ts ├── builtin-components.d.ts ├── dom.d.ts └── v2-compat.d.ts └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * -text 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | tags: [v*] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: "12.x" 16 | registry-url: "https://registry.npmjs.org" 17 | - run: yarn 18 | - run: yarn build 19 | - run: yarn test 20 | # rerun tests with latest vue and typescript 21 | - run: yarn upgrade vue vue-class-component vue-template-compiler typescript --latest 22 | - run: yarn test 23 | # deploy 24 | - if: startsWith( github.ref, 'refs/tags/v' ) 25 | run: yarn publish 26 | env: 27 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # project specific 61 | lib 62 | .vscode 63 | .temp 64 | dist 65 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | src 3 | tsconfig.json 4 | .github 5 | .vscode 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.json 2 | README.md 3 | node_modules 4 | .temp 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "trailingComma": "none", 4 | "arrowParens": "avoid" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 wonderful-panda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/vue-tsx-support.svg)](https://badge.fury.io/js/vue-tsx-support) 2 | [![build](https://github.com/wonderful-panda/vue-tsx-support/workflows/build/badge.svg)](https://github.com/wonderful-panda/vue-tsx-support/actions?query=workflow%3Abuild) 3 | 4 | # vue-tsx-support 5 | TSX (JSX for TypeScript) support library for Vue 6 | 7 | ## :warning: BREAKING CHANGES 8 | 9 | If your project already uses vue-tsx-support v2, see [Migration from V2](#migration-from-v2) section. 10 | 11 | ## TABLE OF CONTENTS 12 | 13 | 14 | - [vue-tsx-support](#vue-tsx-support) 15 | - [:warning: BREAKING CHANGES](#warning-breaking-changes) 16 | - [TABLE OF CONTENTS](#table-of-contents) 17 | - [NEW FEATURES](#new-features) 18 | - [PREREQUISITE](#prerequisite) 19 | - [INSTALLATION](#installation) 20 | - [Migration from V2](#migration-from-v2) 21 | - [USAGE](#usage) 22 | - [Intrinsic elements](#intrinsic-elements) 23 | - [Components](#components) 24 | - [Make existing components tsx-ready.](#make-existing-components-tsx-ready) 25 | - [Writing components by object-style API (Like `Vue.extend`)](#writing-components-by-object-style-api-like-vueextend) 26 | - [Writing component by class-style API (`vue-class-component` and/or `vue-property-decorator`)](#writing-component-by-class-style-api-vue-class-component-andor-vue-property-decorator) 27 | - [1. Extends from `Component` class provided by `vue-tsx-support`](#1-extends-from-component-class-provided-by-vue-tsx-support) 28 | - [2. Add `_tsx` field to tell type information to TypeScript.](#2-add-_tsx-field-to-tell-type-information-to-typescript) 29 | - [Writing component by composition api (`@vue/composition-api`)](#writing-component-by-composition-api-vuecomposition-api) 30 | - [OPTIONS](#options) 31 | - [allow-element-unknown-attrs](#allow-element-unknown-attrs) 32 | - [allow-unknown-props](#allow-unknown-props) 33 | - [enable-html-attrs](#enable-html-attrs) 34 | - [enable-nativeon](#enable-nativeon) 35 | - [enable-vue-router](#enable-vue-router) 36 | - [allow-props-object](#allow-props-object) 37 | - [APIS](#apis) 38 | - [modifiers](#modifiers) 39 | - [Available modifiers](#available-modifiers) 40 | - [LICENSE](#license) 41 | 42 | 43 | 44 | ## NEW FEATURES 45 | 46 | - Typesafe emit for declared events 47 | - @vue/composition-api support 48 | 49 | ## PREREQUISITE 50 | 51 | - Vue >= 2.6.0, < 3.0.0 52 | - TypeScript >= 3.8.0 (3.9.2 or later recommended) 53 | 54 | `vue-tsx-support` does not support Vue 3 because Vue 3 has it's own JSX type checker and there are some incompatibilities with Vue 2. 55 | 56 | If you want to use composition API with `vue-tsx-support`, you can use [@vue/composition-api](https://github.com/vuejs/composition-api). 57 | 58 | ## INSTALLATION 59 | 60 | 1. Create Vue project with TypeScript and babel support. 61 | 62 | vue-tsx-support is a type checker for TypeScript, not a transpiler. 63 | You must install babel presets (@vue/babel-preset-app or @vue/babel-preset-jsx) separatedly. 64 | 65 | Vue CLI may help you. 66 | 67 | - [Installation - Vue.js](https://vuejs.org/v2/guide/installation.html) 68 | - [Vue CLI](https://cli.vuejs.org/) 69 | - [vuejs/jsx](https://github.com/vuejs/jsx) 70 | 71 | :bulb: If you want use @vue/composition-api, `@vue/babel-preset-jsx` >= 1.2.1 or [babel-preset-vue-vca](https://github.com/luwanquan/babel-preset-vca-jsx) is needed. 72 | 73 | 2. Install vue-tsx-support from npm 74 | 75 | ``` 76 | yarn add vue-tsx-support -D 77 | ``` 78 | 79 | 3. In `tsconfig.json`, set `"preserve"` to `jsx` and `"VueTsxSupport"` to `jsxFactory` 80 | 81 | ```json 82 | { 83 | "compilerOptions": { 84 | "jsx": "preserve", 85 | "jsxFactory": "VueTsxSupport", 86 | "...": "..." 87 | }, 88 | "include": [ 89 | "..." 90 | ] 91 | } 92 | ``` 93 | 94 | 4. import `vue-tsx-support/enable-check.d.ts` somewhere, 95 | 96 | ```typescript 97 | import "vue-tsx-support/enable-check" 98 | ``` 99 | 100 | or add it to "include" in `tsconfig.json` 101 | 102 | ```json 103 | { 104 | "compilerOptions": { 105 | "...": "..." 106 | }, 107 | "include": [ 108 | "node_modules/vue-tsx-support/enable-check.d.ts", 109 | "..." 110 | ] 111 | } 112 | ``` 113 | 114 | ### Migration from V2 115 | 116 | 1. In `tsconfig.json`, set `"VueTsxSupport"` to `jsxFactory` 117 | 118 | 2. Enable `allow-props-object` option (Optional) 119 | 120 | 121 | ## USAGE 122 | 123 | ### Intrinsic elements 124 | 125 | Standard HTML elements are defined as intrinsic elements. 126 | So, compiler can check attribute names and attribute types of them: 127 | 128 | ```jsx 129 | // OK 130 |
; 131 | // OK 132 | ; 133 | // OK 134 | ; 135 | // NG: because `href` is not a valid attribute of `div` 136 |
; 137 | // NG: because `id` must be a string 138 |
; 139 | ``` 140 | 141 | Lower case tags are treated as unknown intrinsic element. 142 | TypeScript checks nothing for such tags. 143 | 144 | ```jsx 145 | // OK 146 | 147 | ``` 148 | 149 | ### Components 150 | 151 | Basically, `vue-tsx-support` checks three types for each component. 152 | 153 | - __Prop types__ 154 | Determine name, type, and required or optional of each props. 155 | When using existing component as-is, you must specify prop types manually. 156 | When writing component with APIs of `vue-tsx-support`, prop types are automatically obtained from component definition. 157 | 158 | - __Custom event types (optional)__ 159 | If the component has custom events, you can specify custom event types additionally, 160 | and `vue-tsx-support` will check if event names and argument types are correct or not. 161 | 162 | - __Scoped slot types (optional)__ 163 | If the component has uses scoped slots, you can specify scoped slot types additionally, 164 | and `vue-tsx-support` will check if scoped slot names and argument types are correct or not. 165 | 166 | #### Make existing components tsx-ready. 167 | 168 | By default, `vue-tsx-support` does not allow unknown props. 169 | For example, below code causes compilation error. 170 | 171 | ```jsx 172 | import Vue from "vue"; 173 | import AwesomeButton from "third-party-library/awesome-button"; 174 | 175 | export default Vue.extend({ 176 | render() { 177 | // ERROR: because TypeScript does not know that AwesomeButton has 'text' prop. 178 | return ; 179 | } 180 | }); 181 | ``` 182 | 183 | You can add type information to existing component without modifying component itself, like below: 184 | 185 | ```typescript 186 | import AwesomeButtonOrig from "third-party-library/awesome-button"; 187 | import * as tsx from "vue-tsx-support"; 188 | 189 | type AwesomeButtonProps = { 190 | text: string; 191 | raised?: boolean; 192 | rounded?: boolean; 193 | } 194 | 195 | // Now, AwesomeButton has 1 required prop(text) and 2 optional props(raised, rounded) 196 | export const AwesomeButton = tsx.ofType().convert(AwesomeButtonOrig); 197 | ``` 198 | 199 | You also can specify custom event types as second type parameter, and scoped slot types as third type parameter. 200 | 201 | For example: 202 | 203 | ```typescript 204 | import AwesomeListOrig from "third-party-library/awesome-list"; 205 | import * as tsx from "vue-tsx-support"; 206 | 207 | type Item = { id: string, text: string }; 208 | 209 | type AwesomeListProps = { 210 | items: ReadonlyArray; 211 | rowHeight: number; 212 | } 213 | 214 | type AwesomeListEvents = { 215 | // member name must be ['on' + event name(with capitalizing first charactor)] 216 | onRowClicked: { item: Item, index: number }; 217 | } 218 | 219 | type AwesomeListScopedSlots = { 220 | row: { item: Item } 221 | } 222 | 223 | export const AwesomeList = tsx.ofType< 224 | AwesomeListProps, 225 | AwesomeListEvents, 226 | AwesomeListScopedSlots 227 | >().convert(AwesomeListOrig); 228 | ``` 229 | 230 | Then you can use AwesomeList like below: 231 | 232 | ```jsx 233 | import { VNode } from "vue"; 234 | const App = Vue.extend({ 235 | render(): VNode { 236 | return ( 237 | console.log(`${p.item.text} clicked!`)} 241 | scopedSlots={{ 242 | row: item =>
{item.text}
243 | }} 244 | /> 245 | ); 246 | } 247 | }); 248 | ``` 249 | 250 | #### Writing components by object-style API (Like `Vue.extend`) 251 | 252 | If you use `Vue.extend()`, just replace it by `componentFactory.create` and your component becomes TSX-ready. 253 | 254 | Props type is infered from props definition automatically. 255 | For example, props type will be `{ text: string, important?: boolean }` in below code. 256 | 257 | :warning: In some environment, `as const` may be needed to make prop required properly. 258 | 259 | ```jsx 260 | import { VNode } from "vue"; 261 | import * as tsx from "vue-tsx-support"; 262 | const MyComponent = tsx.componentFactory.create({ 263 | props: { 264 | text: { type: String, required: true }, 265 | important: Boolean, 266 | } as const, // `as const` is needed in some cases. 267 | computed: { 268 | className(): string { 269 | return this.important ? "label-important" : "label-normal"; 270 | } 271 | }, 272 | methods: { 273 | onClick(event: Event) { this.$emit("ok", event); } 274 | }, 275 | render(): VNode { 276 | return {this.text}; 277 | } 278 | }); 279 | ``` 280 | 281 | :bulb: You can use `component` as as shorthand of `componentFactory.create`. 282 | 283 | ```jsx 284 | import * as tsx from "vue-tsx-support"; 285 | const MyComponent = tsx.component({ 286 | /* snip */ 287 | }); 288 | ``` 289 | 290 | If your component has custom events or scoped slots, use `componentFactoryOf` instead. 291 | 292 | ```typescript 293 | import { VNode } from "vue"; 294 | import * as tsx from "vue-tsx-support"; 295 | 296 | type AwesomeListEvents = { 297 | onRowClicked: { item: {}, index: number }; 298 | } 299 | 300 | type AwesomeListScopedSlots = { 301 | row: { item: {} } 302 | } 303 | 304 | export const AwesomeList = tsx.componentFactoryOf< 305 | AwesomeListEvents, 306 | AwesomListScopedSlots 307 | >().create({ 308 | name: "AwesomeList", 309 | props: { 310 | items: { type: Array, required: true }, 311 | rowHeight: { type: Number, required: true } 312 | }, 313 | computed: { /* ... */}, 314 | method: { 315 | emitRowClicked(item: {}, index: number): void { 316 | // Equivalent to `this.$emit("rowClicked", { item, index })`, 317 | // And event name and payload type are statically checked. 318 | tsx.emitOn(this, "onRowClicked", { item, index }); 319 | } 320 | }, 321 | render(): VNode { 322 | return ( 323 |
324 | { 325 | this.visibleItems.map((item, index) => ( 326 |
this.$emit("rowClicked", { item, index })}> 327 | { 328 | // slot name ('row') and argument types are statically checked. 329 | this.$scopedSlots.row({ item }) 330 | } 331 |
332 | ) 333 | } 334 |
335 | ); 336 | } 337 | }); 338 | ``` 339 | 340 | #### Writing component by class-style API (`vue-class-component` and/or `vue-property-decorator`) 341 | 342 | If you prefer class-style component by using `vue-class-component` and/or `vue-property-decorator`, 343 | there are some options to make it tsx-ready. 344 | 345 | ##### 1. Extends from `Component` class provided by `vue-tsx-support` 346 | 347 | ```jsx 348 | import { VNode } from "vue"; 349 | import { Component, Prop } from "vue-property-decorator"; 350 | import * as tsx from "vue-tsx-support"; 351 | 352 | type MyComponentProps = { 353 | text: string; 354 | important?: boolean; 355 | } 356 | 357 | @Component 358 | export class MyComponent extends tsx.Component { 359 | @Prop({ type: String, required: true }) 360 | text!: string; 361 | @Prop(Boolean) 362 | important?: boolean; 363 | 364 | get className() { 365 | return this.important ? "label-important" : "label-normal"; 366 | } 367 | onClick(event: MouseEvent) { 368 | this.$emit("ok", event); 369 | } 370 | render(): VNode { 371 | return {this.text}; 372 | } 373 | } 374 | ``` 375 | 376 | :warning: Unfortunately, `vue-tsx-support` can't infer prop types automatically in this case, so you must write type manually. 377 | 378 | ##### 2. Add `_tsx` field to tell type information to TypeScript. 379 | 380 | ```jsx 381 | import { VNode } from "vue"; 382 | import { Component, Prop } from "vue-property-decorator"; 383 | import * as tsx from "vue-tsx-support"; 384 | 385 | @Component 386 | export class MyComponent extends Vue { 387 | _tsx!: { 388 | // specify props type to `props`. 389 | props: Pick 390 | }; 391 | 392 | @Prop({ type: String, required: true }) 393 | text!: string; 394 | @Prop(Boolean) 395 | important?: boolean; 396 | 397 | get className() { 398 | return this.important ? "label-important" : "label-normal"; 399 | } 400 | render(): VNode { 401 | return {this.text}; 402 | } 403 | } 404 | ``` 405 | 406 | You can use `DeclareProps` instead of `{ props: T }`. 407 | 408 | ```jsx 409 | import { Component, Prop } from "vue-property-decorator"; 410 | import * as tsx from "vue-tsx-support"; 411 | 412 | @Component 413 | export class MyComponent extends Vue { 414 | _tsx!: tsx.DeclareProps>; 415 | 416 | /* ...snip... */ 417 | } 418 | ``` 419 | 420 | :bulb: `PickProps` is more convenient than `Pick` here, it removes attributes from `Vue` from completion candidates. (e.g. `$data`, `$props`, and so on) 421 | 422 | ```jsx 423 | import { Component, Prop } from "vue-property-decorator"; 424 | import * as tsx from "vue-tsx-support"; 425 | 426 | @Component 427 | export class MyComponent extends Vue { 428 | _tsx!: tsx.DeclareProps>; 429 | 430 | /* ...snip... */ 431 | } 432 | ``` 433 | 434 | :bulb: When you can make all data, computed and methods private, you can use `AutoProps` instead. 435 | `AutoProps` picks all public members other than members from component options(`render`, `created` etc). 436 | 437 | ```jsx 438 | import { Component, Prop } from "vue-property-decorator"; 439 | import * as tsx from "vue-tsx-support"; 440 | 441 | @Component 442 | export class MyComponent extends Vue { 443 | _tsx!: tsx.DeclareProps> 444 | 445 | @Prop({ type: String, required: true }) 446 | text!: string; 447 | 448 | @Prop(Boolean) 449 | important?: boolean; 450 | 451 | // data 452 | private count = 0; 453 | // computed 454 | private get className() { 455 | return this.important ? "label-important" : "label-normal"; 456 | } 457 | // methods 458 | private onClick() { 459 | this.count += 1; 460 | } 461 | 462 | render(): VNode { 463 | return ( 464 | 465 | {`${this.text}-${this.count}`} 466 | 467 | ); 468 | } 469 | } 470 | ``` 471 | 472 | :bulb: If your component has custom events, you can specify events handlers type additionally. 473 | 474 | ```jsx 475 | import { Component, Prop } from "vue-property-decorator"; 476 | import * as tsx from "vue-tsx-support"; 477 | 478 | @Component 479 | export class MyComponent extends Vue { 480 | _tsx!: tsx.DeclareProps> & 481 | tsx.DeclareOnEvents<{ onOk: string }>; 482 | 483 | /* ...snip... */ 484 | } 485 | ``` 486 | 487 | :bulb: If your component uses scoped slots, you should add type to `$scopedSlots` by `tsx.InnerScopedSlots`. 488 | 489 | ```jsx 490 | import { Component, Prop } from "vue-property-decorator"; 491 | import * as tsx from "vue-tsx-support"; 492 | 493 | @Component 494 | export class MyComponent extends Vue { 495 | _tsx!: tsx.DeclareProps>; 496 | 497 | $scopedSlots!: tsx.InnerScopedSlots<{ default?: string }>; 498 | 499 | /* ...snip... */ 500 | } 501 | ``` 502 | 503 | #### Writing component by composition api (`@vue/composition-api`) 504 | 505 | Vue 3 is not supported. 506 | To use composition api with Vue 2, You can use `@vue/composition-api`. 507 | 508 | There are 2 babel presets which support JSX syntax with `@vue/composition-api`. 509 | - `@vue/babel-preset-jsx` >= 1.2.1 (You must enable composition-api support explicitly by specifying `{ compositionAPI: true }`) 510 | - `babel-preset-vca-jsx` 511 | 512 | To make TSX-ready component by composition api, use `component` of `vue-tsx-support/lib/vca` instead of `defineComponent` of `@vue/composition-api`. 513 | 514 | ```jsx 515 | import { computed } from "@vue/composition-api"; 516 | import * as vca from "vue-tsx-support/lib/vca"; 517 | 518 | const MyComponent = vca.component({ 519 | name: "MyComponent", 520 | props: { 521 | text: { type: String, required: true }, 522 | important: Boolean, 523 | }, 524 | setup(p) { 525 | const className = computed(() => p.important ? "label-important" : "label-normal"); 526 | return () => ( 527 | {p.text}; 528 | ); 529 | } 530 | }); 531 | ``` 532 | 533 | If your component has custom event or scoped slots, specify them types in 2nd argument of `setup`. 534 | 535 | ```jsx 536 | import { computed, onMounted } from "@vue/composition-api"; 537 | import * as vca from "vue-tsx-support/lib/vca"; 538 | 539 | type AwesomeListEvents = { 540 | onRowClicked: { item: {}, index: number }; 541 | } 542 | 543 | type AwesomeListScopedSlots = { 544 | row: { item: {} } 545 | } 546 | 547 | export const AwesomeList = vca.component({ 548 | name: "AwesomeList", 549 | props: { 550 | items: { type: Array, required: true }, 551 | rowHeight: { type: Number, required: true } 552 | }, 553 | setup(p, ctx: vca.SetupContext) { 554 | const visibleItems = computed(() => ... ); 555 | const emitRowClicked = (item: {}, index: number) => { 556 | // Equivalent to `ctx.emit("rowClicked", { item, index })`, 557 | // And event name and payload type are statically checked. 558 | vca.emitOn(ctx, "onRowClicked", { item, index }); 559 | } 560 | 561 | return () => ( 562 |
563 | { 564 | visibleItems.value.map((item, index) => ( 565 |
emitRowClicked(item, index)}> 566 | { 567 | // slot name ('row') and argument types are statically checked. 568 | ctx.slots.row({ item }) 569 | } 570 |
571 | ) 572 | } 573 |
574 | ); 575 | } 576 | }); 577 | ``` 578 | 579 | ## OPTIONS 580 | 581 | `vue-tsx-support` has some options which change behaviour globally. 582 | See under the `options` directory. 583 | 584 | To enable each options, import them somewhere 585 | 586 | ```typescript 587 | // enable `allow-unknown-props` option 588 | import "vue-tsx-support/options/allow-unknown-props"; 589 | ``` 590 | 591 | :warning: Scope of option is whole project, not a file. 592 | 593 | ### allow-element-unknown-attrs 594 | 595 | Make enabled to specify unknown attributes to intrinsic elements 596 | 597 | ```jsx 598 | // OK:`foo` is unknown attribute, but can be compiled 599 |
; 600 | ``` 601 | 602 | ### allow-unknown-props 603 | 604 | Make enabled to specify unknown props to Vue component. 605 | 606 | ```jsx 607 | const MyComponent = vuetsx.createComponent<{ foo: string }>({ /* ... */ }); 608 | // OK: `bar` is unknown prop, but can be compiled 609 | ; 610 | ``` 611 | 612 | ### enable-html-attrs 613 | 614 | Make enabled to specify HTML attributes to Vue component. 615 | 616 | ```jsx 617 | const MyComponent = vuetsx.createComponent<{ foo: string }>({ /* ... */ }); 618 | // OK: `min` and `max` are valid HTML attributes 619 | ; 620 | // NG: compiler checks type of `min` (`min` must be number) 621 | ; 622 | ``` 623 | 624 | ### enable-nativeon 625 | 626 | Make enabled to specify native event listeners to Vue component. 627 | 628 | ```jsx 629 | const MyComponent = vuetsx.createComponent<{ foo: string }>({ /* ... */ }); 630 | // OK 631 | ... } />; // and `e` is infered as MouseEvent 632 | ``` 633 | 634 | ### enable-vue-router 635 | 636 | Add definitions of `router-link` and `router-view` 637 | 638 | ### allow-props-object 639 | 640 | Make enabled to pass props as "props". 641 | 642 | ```jsx 643 | const MyComponent = vuetsx.createComponent<{ foo: string }>({ /* ... */ }); 644 | // OK 645 | ; 646 | ``` 647 | 648 | ## APIS 649 | 650 | ### modifiers 651 | 652 | Event handler wrappers which work like some event modifiers available in template 653 | 654 | ```typescript 655 | import { modifiers as m } from "vue-tsx-support"; 656 | 657 | // Basic usage: 658 | // Equivalent to `
` 659 |
; 660 | 661 | // Use multiple modifiers: 662 | // Equivalent to `
` 663 |
; 664 | 665 | // Use without event handler: 666 | // Equivalent to `
` 667 |
; 668 | 669 | // Use multiple keys: 670 | // Equivalent to `
` 671 |
; 672 | 673 | // Use exact modkey combination: 674 | // Equivalent to `
` 675 |
; 676 | ``` 677 | 678 | #### Available modifiers 679 | 680 | * `esc`, `tab`, `enter`, `space`, `up`, `down`, `del`, `left`, `right` 681 | 682 | Execute event handler only when specified key is pressed. 683 | :warning: `del` allows not only DELETE, but also BACKSPACE. 684 | :warning: `left` and `right` have another behavior when specified to mouse event 685 | :warning: combination of key modifiers (e.g. `m.enter.esc`) does not work. See [keys](#keys) 686 | 687 | * `left`, `right`, `middle` 688 | 689 | Execute event handler only when specified mouse button is pressed. 690 | :warning: `left` and `right` have another behavior when specified to keyboard event 691 | 692 | * `ctrl`, `shift`, `alt`, `meta` 693 | 694 | Execute event handler only when specified system modifier key is pressed. 695 | 696 | * `noctrl`, `noshift`, `noalt`, `nometa` 697 | 698 | Execute event handler only when specified system modifier key is not pressed. 699 | 700 | * `self` 701 | 702 | Execute event handler only when event.target is the element itself (not from children). 703 | 704 | * `prevent`, `stop` 705 | 706 | Call `preventDefault` or `stopPropagation` of event object before executing event handler. 707 | 708 | 709 | * `keys(...args)` 710 | 711 | Execute event handler only when one of specified key is pressed. 712 | Known key name("esc", "tab", "enter", ...) or number can be specified. 713 | 714 | ```typescript 715 | // when enter or esc pressed 716 |
; 717 | // when 'a' pressed 718 |
; 719 | ``` 720 | 721 | * `exact(...args)` 722 | 723 | Execute event handler only when specified system modifier keys are all pressed, and others are not pressed. 724 | 725 | ```typescript 726 | // when CTRL, SHIFT are both pressed, and ALT, META are both not pressed 727 |
; 728 | ``` 729 | 730 | ## LICENSE 731 | 732 | MIT 733 | 734 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // used for jest only 2 | module.exports = { 3 | presets: ["@babel/env"] 4 | }; 5 | -------------------------------------------------------------------------------- /enable-check.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import * as base from "./types/base"; 3 | import * as builtin from "./types/builtin-components"; 4 | 5 | declare global { 6 | namespace VueTsxSupport.JSX { 7 | interface Element extends base.Element {} 8 | interface ElementClass extends base.ElementClass {} 9 | type LibraryManagedAttributes = C extends new () => infer V 10 | ? base.CombinedTsxComponentAttrs< 11 | base.AttributesOf, 12 | base.PropsOf, 13 | base.PrefixedEventsOf, 14 | base.OnOf, 15 | V extends { $scopedSlots: infer X } ? X : {}, 16 | base.IsPropsObjectAllowed 17 | > & 18 | (V extends { _tsxattrs: infer T } ? T : {}) 19 | : P; 20 | 21 | interface IntrinsicElements extends base.IntrinsicElements { 22 | // allow unknown elements 23 | [name: string]: any; 24 | 25 | // builtin components 26 | transition: base.CombinedTsxComponentAttrs; 27 | "transition-group": base.CombinedTsxComponentAttrs< 28 | builtin.TransitionGroupProps, 29 | {}, 30 | {}, 31 | {}, 32 | {}, 33 | true 34 | >; 35 | "keep-alive": base.CombinedTsxComponentAttrs; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /enable-check.js: -------------------------------------------------------------------------------- 1 | // dummy js to avoid import error 2 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ["js", "ts", "tsx", "json"], 3 | moduleNameMapper: { 4 | "^vue$": "/node_modules/vue/dist/vue.js" 5 | }, 6 | testRegex: "test/jest/(.*)\\.tsx?$", 7 | transform: { 8 | "^.+\\.jsx?$": "babel-jest", 9 | "^.+\\.tsx?$": "ts-jest" 10 | }, 11 | globals: { 12 | "ts-jest": { 13 | tsconfig: "test/jest/tsconfig.json", 14 | babelConfig: { 15 | presets: ["@babel/env", "@vue/babel-preset-jsx"] 16 | } 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /options/allow-element-unknown-attrs.d.ts: -------------------------------------------------------------------------------- 1 | // Make enabled to specify unknown attributes to the intrinsic elements 2 | import ".."; 3 | 4 | declare module "vue-tsx-support/types/base" { 5 | interface ElementAdditionalAttrs { 6 | [name: string]: any; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /options/allow-props-object.d.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | 3 | declare module "vue/types/vue" { 4 | interface Vue { 5 | _tsx_allowPropsObject: true; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /options/allow-unknown-props.d.ts: -------------------------------------------------------------------------------- 1 | // Make enabled to specify unknown attributes(props) to the Vue components 2 | declare namespace VueTsxSupport.JSX { 3 | interface IntrinsicAttributes { 4 | [name: string]: any; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /options/enable-html-attrs.d.ts: -------------------------------------------------------------------------------- 1 | // Make enabled to specify HTML attributes to the Vue component 2 | import { AllHTMLAttributes } from "../types/dom"; 3 | 4 | declare global { 5 | namespace VueTsxSupport.JSX { 6 | interface IntrinsicAttributes extends AllHTMLAttributes {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /options/enable-nativeon.d.ts: -------------------------------------------------------------------------------- 1 | // Make enabled to specify native event listeners to the Vue component 2 | import { EventsNativeOn, Events } from "../types/dom"; 3 | import { EventHandlers } from "../types/base"; 4 | import { VNodeData } from "vue"; 5 | 6 | declare global { 7 | namespace VueTsxSupport.JSX { 8 | interface IntrinsicAttributes extends EventHandlers { 9 | nativeOn?: EventHandlers & VNodeData["nativeOn"]; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /options/enable-vue-router.d.ts: -------------------------------------------------------------------------------- 1 | // Add definitions of `router-link` and `router-view` 2 | 3 | import { CombinedTsxComponentAttrs } from "../types/base"; 4 | import { Location } from "vue-router"; 5 | 6 | interface RouterLinkProps { 7 | to: string | Location; 8 | tag?: string; 9 | exact?: boolean; 10 | append?: boolean; 11 | replace?: boolean; 12 | activeClass?: string; 13 | exactActiveClass?: string; 14 | event?: string | string[]; 15 | } 16 | 17 | interface RouterViewProps { 18 | name?: string; 19 | } 20 | 21 | declare global { 22 | namespace VueTsxSupport.JSX { 23 | interface IntrinsicElements { 24 | "router-link"?: CombinedTsxComponentAttrs; 25 | "router-view"?: CombinedTsxComponentAttrs; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-tsx-support", 3 | "version": "3.2.0", 4 | "description": "TSX (JSX for TypeScript) support library for Vue", 5 | "main": "dist/cjs/index.js", 6 | "module": "dist/esm/index.js", 7 | "typings": "lib/index.d.ts", 8 | "exports": { 9 | ".": { 10 | "import": "./dist/esm/index.js", 11 | "require": "./dist/cjs/index.js", 12 | "default": "./dist/esm/index.js" 13 | }, 14 | "./lib/*": { 15 | "import": "./dist/esm/*.js", 16 | "require": "./dist/cjs/*.js", 17 | "default": "./dist/esm/*.js" 18 | } 19 | }, 20 | "scripts": { 21 | "clean": "rimraf lib dist test/tsc/**/.temp", 22 | "build:esm": "tsc -p tsconfig.json --outDir dist/esm --module esnext", 23 | "build:cjs": "tsc -p tsconfig.json --outDir dist/cjs --module commonjs", 24 | "build": "npm-run-all build:*", 25 | "prettier": "prettier --list-different \"{src,types,options,test}/**/*.{ts,tsx}\"", 26 | "prettier:fix": "prettier --write \"{src,types,options,test}/**/*.{ts,tsx}\"", 27 | "jest": "jest", 28 | "tsc-test": "node test/tsc/runner.js", 29 | "test": "npm-run-all prettier tsc-test jest" 30 | }, 31 | "repository": { 32 | "type": "git", 33 | "url": "git+https://github.com/wonderful-panda/vue-tsx-support.git" 34 | }, 35 | "keywords": [ 36 | "Vue", 37 | "TypeScript", 38 | "JSX", 39 | "TSX" 40 | ], 41 | "author": "Iwata Hidetaka ", 42 | "license": "MIT", 43 | "bugs": { 44 | "url": "https://github.com/wonderful-panda/vue-tsx-support/issues" 45 | }, 46 | "homepage": "https://github.com/wonderful-panda/vue-tsx-support#readme", 47 | "devDependencies": { 48 | "@babel/core": "^7.12.3", 49 | "@babel/plugin-syntax-jsx": "^7.12.1", 50 | "@babel/preset-env": "^7.12.1", 51 | "@types/jest": "^26.0.15", 52 | "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", 53 | "@vue/babel-preset-jsx": "^1.2.4", 54 | "@vue/composition-api": "^1.0.0-beta.19", 55 | "@vue/test-utils": "1.1.1", 56 | "babel-jest": "26.6.3", 57 | "glob": "^7.1.6", 58 | "hoek": "^6.1.3", 59 | "jest": "26.6.3", 60 | "lodash": "^4.17.20", 61 | "mixin-deep": "^2.0.0", 62 | "npm-run-all": "^4.1.5", 63 | "prettier": "2.1.2", 64 | "rimraf": "^3.0.2", 65 | "ts-jest": "^26.4.4", 66 | "typescript": "3.9.2", 67 | "vue": "2.6.12", 68 | "vue-class-component": "^7.2.6", 69 | "vue-property-decorator": "^9.0.2", 70 | "vue-router": "^3.4.9", 71 | "vue-template-compiler": "2.6.12" 72 | }, 73 | "peerDependencies": { 74 | "typescript": ">=3.8.0", 75 | "vue": ">=2.6.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/advance.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import { _TsxComponentV3 } from "./api"; 3 | import { PropsOf, PrefixedEventsOf } from "../types/base"; 4 | 5 | export type ComponentProps = PropsOf>; 6 | export type ComponentEvents = PrefixedEventsOf>; 7 | export type WithProps = V extends _TsxComponentV3< 8 | infer V, 9 | infer Attributes, 10 | any, 11 | infer PrefixedEvents, 12 | infer On, 13 | infer ScopedSlotArgs 14 | > 15 | ? _TsxComponentV3 16 | : _TsxComponentV3; 17 | 18 | export type WithEvents = V extends _TsxComponentV3< 19 | infer V, 20 | infer Attributes, 21 | infer Props, 22 | any, 23 | infer On, 24 | infer ScopedSlotArgs 25 | > 26 | ? _TsxComponentV3 27 | : _TsxComponentV3; 28 | -------------------------------------------------------------------------------- /src/api.ts: -------------------------------------------------------------------------------- 1 | import Vue, { ComponentOptions, FunctionalComponentOptions, VueConstructor, VNodeData } from "vue"; 2 | import { 3 | RecordPropsDefinition, 4 | ThisTypedComponentOptionsWithRecordProps as ThisTypedComponentOptions 5 | } from "vue/types/options"; 6 | 7 | import { 8 | InnerScopedSlots, 9 | TsxComponentTypeInfo, 10 | EventHandler, 11 | EventHandlers, 12 | DeclareOnEvents, 13 | DeclareOn, 14 | DeclareProps 15 | } from "../types/base"; 16 | import { EventsNativeOn, AllHTMLAttributes, Events } from "../types/dom"; 17 | 18 | export type _TsxComponentInstanceV3< 19 | V extends Vue, 20 | Attributes, 21 | Props, 22 | PrefixedEvents, 23 | On, 24 | ScopedSlotArgs 25 | > = { 26 | _tsx: TsxComponentTypeInfo; 27 | $scopedSlots: InnerScopedSlots; 28 | } & V; 29 | 30 | export type _TsxComponentV3< 31 | V extends Vue, 32 | Attributes, 33 | Props, 34 | PrefixedEvents, 35 | On, 36 | ScopedSlotArgs 37 | > = VueConstructor< 38 | _TsxComponentInstanceV3 39 | >; 40 | 41 | export class Component extends Vue { 42 | _tsx!: TsxComponentTypeInfo<{}, Props, PrefixedEvents, {}>; 43 | $scopedSlots!: InnerScopedSlots; 44 | } 45 | 46 | /** 47 | * Create component from component options (Compatible with Vue.extend) 48 | */ 49 | export function createComponent( 50 | opts: ComponentOptions | FunctionalComponentOptions 51 | ): _TsxComponentV3 { 52 | return Vue.extend(opts as any) as any; 53 | } 54 | 55 | export interface Factory { 56 | convert( 57 | componentType: new (...args: any[]) => V 58 | ): _TsxComponentV3; 59 | extendFrom( 60 | componentType: VC 61 | ): _TsxComponentV3, {}, Props, PrefixedEvents, On, ScopedSlots> & 62 | Pick; // take over static members of component 63 | } 64 | 65 | const factoryImpl = { 66 | convert: (c: any) => c, 67 | extendFrom: (c: any) => c 68 | }; 69 | 70 | /** 71 | * Specify Props and Event types of component 72 | * 73 | * Usage: 74 | * // Get TSX-supported component with props(`name`, `value`) and event(`onInput`) 75 | * const NewComponent = tsx.ofType<{ name: string, value: string }, { onInput: string }>.convert(Component); 76 | */ 77 | export function ofType(): Factory< 78 | Props, 79 | PrefixedEvents, 80 | On, 81 | ScopedSlots 82 | > { 83 | return factoryImpl; 84 | } 85 | 86 | export function withNativeOn( 87 | componentType: VC 88 | ): _TsxComponentV3< 89 | InstanceType, 90 | EventHandlers & { nativeOn?: EventHandlers & VNodeData["nativeOn"] }, 91 | {}, 92 | {}, 93 | {}, 94 | {} 95 | > { 96 | return componentType as any; 97 | } 98 | 99 | export function withHtmlAttrs( 100 | componentType: VC 101 | ): _TsxComponentV3, AllHTMLAttributes, {}, {}, {}, {}> { 102 | return componentType as any; 103 | } 104 | 105 | export function withUnknownProps( 106 | componentType: VC 107 | ): _TsxComponentV3, Record, {}, {}, {}, {}> { 108 | return componentType as any; 109 | } 110 | 111 | export function withPropsObject( 112 | componentType: VC 113 | ): _TsxComponentV3< 114 | InstanceType & { 115 | _tsx_allowPropsObject: true; 116 | }, 117 | {}, 118 | {}, 119 | {}, 120 | {}, 121 | {} 122 | > { 123 | return componentType as any; 124 | } 125 | 126 | /** 127 | * Experimental support for new typings introduced from Vue 2.5 128 | * Depending on some private types of vue, which may be changed by upgrade :( 129 | */ 130 | 131 | // If props is 132 | // `{ foo: String, bar: String, baz: { type: String, required: true as true} }` 133 | // then, `RequiredPropNames` is "baz", 134 | export type RequiredPropNames> = { 135 | [K in keyof PropsDef]: PropsDef[K] extends { required: true } ? K : never; 136 | }[Extract]; 137 | 138 | export type PropsForOutside = { 139 | [K in RequiredPropNames]: Props[K]; 140 | } & 141 | { [K in Exclude]?: Props[K] }; 142 | 143 | export interface ComponentFactory< 144 | BaseProps, 145 | PrefixedEvents, 146 | Events, 147 | ScopedSlotArgs, 148 | Super extends Vue 149 | > { 150 | create< 151 | Props, 152 | PropsDef extends RecordPropsDefinition, 153 | RequiredProps extends keyof Props = RequiredPropNames & keyof Props 154 | >( 155 | options: FunctionalComponentOptions>, 156 | requiredProps?: RequiredProps[] 157 | ): _TsxComponentV3< 158 | Super & Props, 159 | {}, 160 | PropsForOutside & BaseProps, 161 | PrefixedEvents, 162 | Events, 163 | ScopedSlotArgs 164 | >; 165 | 166 | create< 167 | Data, 168 | Methods, 169 | Computed, 170 | Props, 171 | PropsDef extends RecordPropsDefinition, 172 | RequiredProps extends keyof Props = RequiredPropNames & keyof Props 173 | >( 174 | options: ThisTypedComponentOptions< 175 | Super & 176 | Vue & { 177 | _tsx?: DeclareProps & BaseProps>; 178 | }, 179 | Data, 180 | Methods, 181 | Computed, 182 | Props 183 | > & { 184 | props?: PropsDef; 185 | }, 186 | requiredPropsNames?: RequiredProps[] 187 | ): _TsxComponentV3< 188 | Super & Data & Methods & Computed & Props, 189 | {}, 190 | PropsForOutside & BaseProps, 191 | PrefixedEvents, 192 | Events, 193 | ScopedSlotArgs 194 | >; 195 | 196 | mixin( 197 | mixinObject: ThisTypedComponentOptions 198 | ): ComponentFactory< 199 | BaseProps & Props, 200 | PrefixedEvents, 201 | Events, 202 | ScopedSlotArgs, 203 | Super & Data & Methods & Computed & Props 204 | >; 205 | 206 | mixin( 207 | mixinObject: VC 208 | ): ComponentFactory< 209 | BaseProps, 210 | PrefixedEvents, 211 | Events, 212 | ScopedSlotArgs, 213 | Super & InstanceType & { $scopedSlots: InnerScopedSlots } 214 | >; 215 | } 216 | 217 | export interface ExtendableComponentFactory< 218 | BaseProps, 219 | EventsWithPrefix, 220 | EventsWithoutPrefix, 221 | ScopedSlotArgs, 222 | Super extends Vue 223 | > extends ComponentFactory< 224 | BaseProps, 225 | EventsWithPrefix, 226 | EventsWithoutPrefix, 227 | ScopedSlotArgs, 228 | Super 229 | > { 230 | extendFrom( 231 | componentType: VC 232 | ): ComponentFactory< 233 | BaseProps, 234 | EventsWithPrefix, 235 | EventsWithoutPrefix, 236 | ScopedSlotArgs, 237 | InstanceType & { $scopedSlots: InnerScopedSlots } 238 | >; 239 | } 240 | 241 | function createComponentFactory( 242 | base: typeof Vue, 243 | mixins: any[] 244 | ): ComponentFactory { 245 | return { 246 | create(options: any): any { 247 | const mergedMixins = options.mixins ? [...options.mixins, ...mixins] : mixins; 248 | return base.extend({ ...options, mixins: mergedMixins }); 249 | }, 250 | mixin(mixinObject: any): any { 251 | return createComponentFactory(base, [...mixins, mixinObject]); 252 | } 253 | }; 254 | } 255 | 256 | function createExtendableComponentFactory(): ExtendableComponentFactory { 257 | return { 258 | create(options: any): any { 259 | return Vue.extend(options); 260 | }, 261 | extendFrom(base: typeof Vue): any { 262 | return createComponentFactory(base, []); 263 | }, 264 | mixin(mixinObject: any): any { 265 | return createComponentFactory(Vue, [mixinObject]); 266 | } 267 | }; 268 | } 269 | 270 | export const componentFactory: ExtendableComponentFactory< 271 | {}, 272 | {}, 273 | {}, 274 | {}, 275 | Vue 276 | > = createExtendableComponentFactory(); 277 | 278 | export function componentFactoryOf< 279 | PrefixedEvents = {}, 280 | ScopedSlotArgs = {}, 281 | On = {} 282 | >(): ComponentFactory< 283 | {}, 284 | PrefixedEvents, 285 | On, 286 | ScopedSlotArgs, 287 | Vue & { 288 | _tsx: TsxComponentTypeInfo<{}, {}, PrefixedEvents, On>; 289 | $scopedSlots: InnerScopedSlots; 290 | } 291 | > { 292 | return componentFactory as any; 293 | } 294 | 295 | /** 296 | * Shorthand of `componentFactory.create` 297 | */ 298 | export const component = componentFactory.create; 299 | export const extendFrom = componentFactory.extendFrom; 300 | 301 | export function emit( 302 | vm: Vue & { _tsx: DeclareOn }, 303 | name: Name, 304 | ...args: Parameters> 305 | ) { 306 | vm.$emit(name, ...args); 307 | } 308 | 309 | export function emitOn( 310 | vm: Vue & { _tsx: DeclareOnEvents }, 311 | name: Name, 312 | ...args: Parameters> 313 | ) { 314 | vm.$emit( 315 | name.replace(/^on[A-Z]/, v => v[2].toLowerCase()), 316 | ...args 317 | ); 318 | } 319 | 320 | export function emitUpdate( 321 | vm: Vue & { _tsx?: DeclareProps }, 322 | name: Name, 323 | value: Props[Name] 324 | ) { 325 | vm.$emit("update:" + name, value); 326 | } 327 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./api"; 2 | export { TsxComponent, TsxComponentAttrs, TsxComponentInstance } from "../types/v2-compat"; 3 | export { modifiers } from "./modifiers"; 4 | export { 5 | DeclareProps, 6 | DeclareOnEvents, 7 | DeclareOn, 8 | DeclareAttributes, 9 | PickProps, 10 | PickOwnProps, 11 | AutoProps, 12 | MakeOptional, 13 | TsxTypeInfoOf, 14 | ScopedSlotHandlers, 15 | InnerScopedSlot, 16 | InnerScopedSlots 17 | } from "../types/base"; 18 | export { EventsOn, EventsNativeOn, AllHTMLAttributes } from "../types/dom"; 19 | -------------------------------------------------------------------------------- /src/modifiers.ts: -------------------------------------------------------------------------------- 1 | export type EventFilter = (event: Event) => boolean; 2 | export type EventHandler = (event: E) => void; 3 | 4 | export type ModKey = "ctrl" | "shift" | "alt" | "meta"; 5 | 6 | export type KeyModifierName = 7 | | "esc" 8 | | "tab" 9 | | "enter" 10 | | "space" 11 | | "up" 12 | | "down" 13 | | "del" 14 | | "left" 15 | | "right"; 16 | export type MouseModifierName = "left" | "right" | "middle"; 17 | export type ModKeyModifierName = ModKey | "noctrl" | "noshift" | "noalt" | "nometa"; 18 | export type StandaloneModifierName = "prevent" | "stop" | "self"; 19 | export type ModifierName = 20 | | KeyModifierName 21 | | MouseModifierName 22 | | ModKeyModifierName 23 | | StandaloneModifierName 24 | | "keys" 25 | | "exact"; 26 | 27 | // prettier-ignore 28 | export type NextKeys = 29 | K extends KeyModifierName | MouseModifierName | "keys" ? Exclude 30 | : K extends "exact" ? Exclude 31 | : K extends "ctrl" | "noctrl" ? Exclude 32 | : K extends "shift" | "noshift" ? Exclude 33 | : K extends "alt" | "noalt" ? Exclude 34 | : K extends "meta" | "nometa" ? Exclude 35 | : Exclude; 36 | 37 | // prettier-ignore 38 | export type Modifier = { 39 | (handler: EventHandler): EventHandler; 40 | (event: Event): void; // Modifier itself can behave as EventHandler 41 | } & { 42 | [K in Keys]: 43 | K extends "keys" ? (...keys: (KeyModifierName | number)[]) => Modifier> 44 | : K extends "exact" ? (...keys: ModKey[]) => Modifier> 45 | : Modifier> 46 | }; 47 | 48 | function handleEvent(event: Event, filters: EventFilter[], handler?: EventHandler) { 49 | for (let filter of filters) { 50 | if (!filter(event)) { 51 | return; 52 | } 53 | } 54 | if (handler) { 55 | handler(event); 56 | } 57 | } 58 | 59 | const keyCodes: { [K in KeyModifierName]: number | [number, number] } = { 60 | esc: 27, 61 | tab: 9, 62 | enter: 13, 63 | space: 32, 64 | up: 38, 65 | down: 40, 66 | del: [8, 46], 67 | left: 37, 68 | right: 39 69 | }; 70 | 71 | function createKeyFilter(keys: (KeyModifierName | number)[]): EventFilter { 72 | const codes = [] as number[]; 73 | for (const key of keys) { 74 | if (typeof key === "number") { 75 | codes.push(key); 76 | } else { 77 | const code = keyCodes[key]; 78 | if (typeof code === "number") { 79 | codes.push(code); 80 | } else { 81 | codes.push(...code); 82 | } 83 | } 84 | } 85 | switch (codes.length) { 86 | case 0: 87 | return _ => false; 88 | case 1: 89 | const code = codes[0]; 90 | return (e: any) => e.keyCode === code; 91 | default: 92 | return (e: any) => codes.indexOf(e.keyCode) >= 0; 93 | } 94 | } 95 | 96 | interface ChildModifierFilter { 97 | keyboard: boolean; 98 | mouse: boolean; 99 | modkey: boolean; 100 | exact: boolean; 101 | } 102 | 103 | function defineChildModifier( 104 | target: Function, 105 | currentFilters: EventFilter[], 106 | name: string, 107 | filter: EventFilter, 108 | children: ChildModifierFilter 109 | ) { 110 | Object.defineProperty(target, name, { 111 | get: function () { 112 | // call this getter at most once. 113 | // reuse created instance after next time. 114 | const ret = createModifier([...currentFilters, filter], children); 115 | Object.defineProperty(target, name, { 116 | value: ret, 117 | enumerable: true 118 | }); 119 | return ret; 120 | }, 121 | enumerable: true, 122 | configurable: true 123 | }); 124 | } 125 | 126 | function defineKeyCodeModifiers( 127 | target: Function, 128 | filters: EventFilter[], 129 | children: ChildModifierFilter 130 | ) { 131 | for (const name in keyCodes) { 132 | const keyName = name as KeyModifierName; 133 | if (keyName === "left" || keyName === "right") { 134 | continue; 135 | } 136 | const code = keyCodes[keyName]; 137 | if (typeof code === "number") { 138 | defineChildModifier(target, filters, keyName, (e: any) => e.keyCode === code, children); 139 | } else { 140 | const [c1, c2] = code; 141 | defineChildModifier( 142 | target, 143 | filters, 144 | keyName, 145 | (e: any) => e.keyCode === c1 || e.keyCode === c2, 146 | children 147 | ); 148 | } 149 | } 150 | } 151 | 152 | function defineKeys(target: Function, filters: EventFilter[], children: ChildModifierFilter) { 153 | Object.defineProperty(target, "keys", { 154 | get() { 155 | const keysFunction = (...args: (KeyModifierName | number)[]) => { 156 | const propName = "keys:" + args.toString(); 157 | const modifier = this[propName]; 158 | if (modifier !== undefined) { 159 | return modifier; 160 | } 161 | const filter = createKeyFilter(args); 162 | defineChildModifier(this, filters, propName, filter, children); 163 | return this[propName]; 164 | }; 165 | Object.defineProperty(this, "keys", { 166 | value: keysFunction, 167 | enumerable: true 168 | }); 169 | return keysFunction; 170 | }, 171 | enumerable: true, 172 | configurable: true 173 | }); 174 | } 175 | 176 | function defineExact(target: Function, filters: EventFilter[], children: ChildModifierFilter) { 177 | Object.defineProperty(target, "exact", { 178 | get() { 179 | const exactFunction = (...args: ModKey[]) => { 180 | const propName = "exact:" + args.toString(); 181 | const modifier = this[propName]; 182 | if (modifier !== undefined) { 183 | return modifier; 184 | } 185 | const expected = { 186 | ctrl: false, 187 | shift: false, 188 | alt: false, 189 | meta: false 190 | }; 191 | args.forEach(arg => (expected[arg] = true)); 192 | const filter = (e: any) => 193 | !!e.ctrlKey === expected.ctrl && 194 | !!e.shiftKey === expected.shift && 195 | !!e.altKey === expected.alt && 196 | !!e.metaKey === expected.meta; 197 | defineChildModifier(this, filters, propName, filter, children); 198 | return this[propName]; 199 | }; 200 | Object.defineProperty(this, "exact", { 201 | value: exactFunction, 202 | enumerable: true 203 | }); 204 | return exactFunction; 205 | }, 206 | enumerable: true, 207 | configurable: true 208 | }); 209 | } 210 | 211 | function createModifier( 212 | filters: EventFilter[], 213 | children: ChildModifierFilter 214 | ): Modifier { 215 | function m(arg: any): any { 216 | if (arg instanceof Function) { 217 | // EventHandler => EventHandler 218 | return (event: Event) => handleEvent(event, filters, arg); 219 | } else { 220 | // Event => void 221 | handleEvent(arg, filters); 222 | return; 223 | } 224 | } 225 | if (children.keyboard || children.mouse) { 226 | const nextChildren = { ...children, keyboard: false, mouse: false }; 227 | if (children.keyboard) { 228 | defineKeyCodeModifiers(m, filters, nextChildren); 229 | defineKeys(m, filters, nextChildren); 230 | } 231 | if (children.mouse) { 232 | defineChildModifier(m, filters, "middle", (e: any) => e.button === 1, nextChildren); 233 | } 234 | defineChildModifier( 235 | m, 236 | filters, 237 | "left", 238 | (e: any) => e.keyCode === 37 || e.button === 0, 239 | nextChildren 240 | ); 241 | defineChildModifier( 242 | m, 243 | filters, 244 | "right", 245 | (e: any) => e.keyCode === 39 || e.button === 2, 246 | nextChildren 247 | ); 248 | } 249 | if (children.exact) { 250 | const nextChildren = { ...children, exact: false, modkey: false }; 251 | defineExact(m, filters, nextChildren); 252 | } 253 | if (children.modkey) { 254 | const nextChildren = { ...children, exact: false }; 255 | defineChildModifier(m, filters, "ctrl", (e: any) => e.ctrlKey, nextChildren); 256 | defineChildModifier(m, filters, "shift", (e: any) => e.shiftKey, nextChildren); 257 | defineChildModifier(m, filters, "alt", (e: any) => e.altKey, nextChildren); 258 | defineChildModifier(m, filters, "meta", (e: any) => e.metaKey, nextChildren); 259 | 260 | defineChildModifier(m, filters, "noctrl", (e: any) => !e.ctrlKey, nextChildren); 261 | defineChildModifier(m, filters, "noshift", (e: any) => !e.shiftKey, nextChildren); 262 | defineChildModifier(m, filters, "noalt", (e: any) => !e.altKey, nextChildren); 263 | defineChildModifier(m, filters, "nometa", (e: any) => !e.metaKey, nextChildren); 264 | } 265 | defineChildModifier( 266 | m, 267 | filters, 268 | "stop", 269 | e => { 270 | e.stopPropagation(); 271 | return true; 272 | }, 273 | children 274 | ); 275 | defineChildModifier( 276 | m, 277 | filters, 278 | "prevent", 279 | e => { 280 | e.preventDefault(); 281 | return true; 282 | }, 283 | children 284 | ); 285 | defineChildModifier(m, filters, "self", e => e.target === e.currentTarget, children); 286 | return m as Modifier; 287 | } 288 | 289 | export const modifiers = createModifier([], { 290 | keyboard: true, 291 | mouse: true, 292 | modkey: true, 293 | exact: true 294 | }); 295 | -------------------------------------------------------------------------------- /src/vca.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode, ComponentOptions } from "vue"; 2 | import { 3 | defineComponent, 4 | SetupContext as SetupContext_, 5 | ComponentRenderProxy, 6 | ShallowUnwrapRef 7 | } from "@vue/composition-api"; 8 | import { _TsxComponentV3, RequiredPropNames, PropsForOutside } from "./api"; 9 | import { RecordPropsDefinition } from "vue/types/options"; 10 | import { InnerScopedSlots, TsxComponentTypeInfo, EventHandler } from "../types/base"; 11 | 12 | export type SetupContext< 13 | PrefixedEvents = unknown, 14 | ScopedSlots = unknown, 15 | On = unknown 16 | > = SetupContext_ & { 17 | slots: InnerScopedSlots; 18 | _tsx?: TsxComponentTypeInfo<{}, {}, PrefixedEvents, On>; 19 | }; 20 | 21 | export type CompositionComponentOptions< 22 | Props, 23 | PropsDef extends RecordPropsDefinition, 24 | PrefixedEvents, 25 | ScopedSlots, 26 | On 27 | > = { 28 | props?: PropsDef & RecordPropsDefinition; 29 | setup: ( 30 | this: void, 31 | props: Props, 32 | ctx: SetupContext 33 | ) => () => VNode; 34 | } & Pick< 35 | ComponentOptions, 36 | "name" | "components" | "comments" | "inheritAttrs" | "directives" | "filters" 37 | >; 38 | 39 | export type CompositionComponentOptionsWithRender< 40 | Props, 41 | PropsDef extends RecordPropsDefinition, 42 | PrefixedEvents, 43 | ScopedSlots, 44 | On, 45 | RawBinding 46 | > = { 47 | props?: PropsDef & RecordPropsDefinition; 48 | setup: ( 49 | this: void, 50 | props: Props, 51 | ctx: SetupContext 52 | ) => RawBinding; 53 | render(): VNode; 54 | } & Pick< 55 | ComponentOptions, 56 | "name" | "components" | "comments" | "inheritAttrs" | "directives" | "filters" 57 | > & 58 | ThisType>; 59 | 60 | export function component< 61 | Props, 62 | PropsDef extends RecordPropsDefinition, 63 | PrefixedEvents, 64 | ScopedSlots, 65 | On, 66 | RequiredProps extends keyof Props = RequiredPropNames & keyof Props 67 | >( 68 | options: CompositionComponentOptions 69 | ): _TsxComponentV3, PrefixedEvents, On, ScopedSlots>; 70 | export function component< 71 | Props, 72 | PropsDef extends RecordPropsDefinition, 73 | PrefixedEvents, 74 | ScopedSlots, 75 | On, 76 | RawBinding, 77 | RequiredProps extends keyof Props = RequiredPropNames & keyof Props 78 | >( 79 | options: CompositionComponentOptionsWithRender< 80 | Props, 81 | PropsDef, 82 | PrefixedEvents, 83 | ScopedSlots, 84 | On, 85 | RawBinding 86 | > 87 | ): _TsxComponentV3< 88 | Vue & ShallowUnwrapRef, 89 | {}, 90 | PropsForOutside, 91 | PrefixedEvents, 92 | On, 93 | ScopedSlots 94 | >; 95 | export function component(options: any) { 96 | return defineComponent(options) as any; 97 | } 98 | 99 | export function emit( 100 | ctx: SetupContext, 101 | name: Name, 102 | ...args: Parameters> 103 | ) { 104 | ctx.emit(name, ...args); 105 | } 106 | 107 | export function emitOn( 108 | ctx: SetupContext, 109 | name: Name, 110 | ...args: Parameters> 111 | ) { 112 | ctx.emit( 113 | name.replace(/^on[A-Z]/, v => v[2].toLowerCase()), 114 | ...args 115 | ); 116 | } 117 | 118 | export function updateEmitter() { 119 | return (ctx: SetupContext, name: K, value: Props[K]) => { 120 | ctx.emit("update:" + name, value); 121 | }; 122 | } 123 | -------------------------------------------------------------------------------- /test/jest/classComponent.tsx: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import Vue, { VNode } from "vue"; 3 | import { Component, Prop } from "vue-property-decorator"; 4 | import { DeclareProps, PickProps, InnerScopedSlots, DeclareOn, emit } from "vue-tsx-support"; 5 | 6 | describe("classComponent", () => { 7 | @Component 8 | class Test extends Vue { 9 | @Prop({ type: String, required: true }) 10 | foo!: string; 11 | @Prop({ type: String }) 12 | bar?: string; 13 | 14 | _tsx!: DeclareProps> & DeclareOn<{ customEvent: string }>; 15 | 16 | $scopedSlots!: InnerScopedSlots<{ default?: string }>; 17 | 18 | emitCustomEvent(arg: string) { 19 | emit(this, "customEvent", arg); 20 | } 21 | 22 | render(): VNode { 23 | const defaultSlot = this.$scopedSlots.default; 24 | const content = defaultSlot ? defaultSlot(this.foo) : this.foo; 25 | return
{content}
; 26 | } 27 | } 28 | describe("create", () => { 29 | it("render", () => { 30 | const w = mount(Test, { 31 | propsData: { foo: "fooValue" }, 32 | scopedSlots: { 33 | default(prop: string) { 34 | return {prop}; 35 | } 36 | } 37 | }); 38 | expect(w.html()).toBe("
fooValue
"); 39 | }); 40 | it("event should be emitted with name without `on` prefix", () => { 41 | const w = mount(Test, { 42 | propsData: { foo: "fooValue" } 43 | }); 44 | w.vm.emitCustomEvent("emit-test"); 45 | expect(w.emitted()).toEqual({ customEvent: [["emit-test"]] }); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/jest/componentFactory.tsx: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import Vue, { VNode } from "vue"; 3 | import * as tsx from "vue-tsx-support"; 4 | 5 | describe("componentFactory", () => { 6 | describe("create", () => { 7 | it("simple component", () => { 8 | const MyComponent = tsx.component( 9 | { 10 | props: { 11 | foo: String, 12 | bar: { type: String, default: "barDefault" } 13 | }, 14 | render(): VNode { 15 | return {this.foo + " " + this.bar}; 16 | } 17 | }, 18 | ["foo"] 19 | ); 20 | 21 | const w = mount(MyComponent, { propsData: { foo: "fooValue" } }); 22 | expect(w.html()).toBe("fooValue barDefault"); 23 | }); 24 | 25 | it("scoped slot", () => { 26 | const ChildComponent = tsx.componentFactoryOf<{}, { default: string }>().create({ 27 | props: { 28 | foo: String 29 | }, 30 | render(): VNode { 31 | return
{this.$scopedSlots.default(this.foo)}
; 32 | } 33 | }); 34 | 35 | const ParentComponent = tsx.component({ 36 | render(): VNode { 37 | return ( 38 | [{v}] 42 | }} 43 | /> 44 | ); 45 | } 46 | }); 47 | 48 | const w = mount(ParentComponent); 49 | expect(w.html()).toBe("
fooValue
"); 50 | }); 51 | }); 52 | 53 | describe("extendFrom", () => { 54 | it("accessing base component members", () => { 55 | const BaseComponent = tsx.component({ 56 | props: { 57 | foo: String 58 | }, 59 | computed: { 60 | bar() { 61 | return "bar"; 62 | } 63 | } 64 | }); 65 | 66 | const ExtendedComponent = tsx.extendFrom(BaseComponent).create({ 67 | props: { 68 | baz: String 69 | }, 70 | render(): VNode { 71 | return {this.foo + this.bar + this.baz}; 72 | } 73 | }); 74 | 75 | const w = mount(ExtendedComponent, { 76 | propsData: { foo: "foo", baz: "baz" } 77 | }); 78 | expect(w.html()).toBe("foobarbaz"); 79 | }); 80 | }); 81 | 82 | describe("mixin", () => { 83 | it("accessing mixin's member", () => { 84 | const Mixin = { 85 | props: { 86 | foo: String 87 | }, 88 | computed: { 89 | bar() { 90 | return "bar"; 91 | } 92 | } 93 | }; 94 | 95 | const Component = tsx.componentFactory.mixin(Mixin).create({ 96 | props: { 97 | baz: String 98 | }, 99 | render(): VNode { 100 | return {this.foo + this.bar + this.baz}; 101 | } 102 | }); 103 | 104 | const w = mount(Component, { propsData: { foo: "foo", baz: "baz" } }); 105 | expect(w.html()).toBe("foobarbaz"); 106 | }); 107 | it("multiple mixins", () => { 108 | const Mixin1 = { 109 | data() { 110 | return { foo: "foo" }; 111 | } 112 | }; 113 | const Mixin2 = Vue.extend({ 114 | data() { 115 | return { bar: "bar" }; 116 | } 117 | }); 118 | 119 | const Component = tsx.componentFactory 120 | .mixin(Mixin1) 121 | .mixin(Mixin2) 122 | .create({ 123 | props: { 124 | baz: String 125 | }, 126 | render(): VNode { 127 | return {this.foo + this.bar + this.baz}; 128 | } 129 | }); 130 | 131 | const w = mount(Component, { propsData: { baz: "baz" } }); 132 | expect(w.html()).toBe("foobarbaz"); 133 | }); 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /test/jest/modifiers.tsx: -------------------------------------------------------------------------------- 1 | import { mount } from "@vue/test-utils"; 2 | import Vue, { VNode } from "vue"; 3 | import { modifiers as m } from "vue-tsx-support"; 4 | 5 | const buttonNames = ["left", "middle", "right"]; 6 | 7 | const keyNames: { [key: number]: string } = { 8 | 27: "esc", 9 | 9: "tab", 10 | 13: "enter", 11 | 32: "space", 12 | 38: "up", 13 | 40: "down", 14 | 8: "delete", 15 | 46: "backspace", 16 | 37: "left", 17 | 39: "right" 18 | }; 19 | describe("modifiers", () => { 20 | it("keys", () => { 21 | const spy = jest.fn(); 22 | const Comp = Vue.extend({ 23 | render(): VNode { 24 | return
spy(keyNames[e.keyCode]))} />; 25 | } 26 | }); 27 | const w = mount(Comp); 28 | w.trigger("keydown.up"); 29 | w.trigger("keydown.down"); 30 | w.trigger("keydown.left"); 31 | w.trigger("keydown.right"); 32 | expect(spy.mock.calls).toEqual([["up"], ["left"]]); 33 | }); 34 | it("left/right (keyboard or mouse)", () => { 35 | const keysL = jest.fn(); 36 | const buttonsL = jest.fn(); 37 | const keysR = jest.fn(); 38 | const buttonsR = jest.fn(); 39 | const Comp = Vue.extend({ 40 | render(): VNode { 41 | return ( 42 |
43 |
keysL(keyNames[e.keyCode]))} 46 | onClick={m.left(e => buttonsL(buttonNames[e.button]))} 47 | /> 48 |
keysR(keyNames[e.keyCode]))} 51 | onClick={m.right(e => buttonsR(buttonNames[e.button]))} 52 | /> 53 |
54 | ); 55 | } 56 | }); 57 | const w = mount(Comp); 58 | const [l, r] = [w.find(".left"), w.find(".right")]; 59 | l.trigger("keydown.left"); 60 | l.trigger("keydown.right"); 61 | l.trigger("click", { button: 0 }); 62 | l.trigger("click", { button: 2 }); 63 | expect(keysL.mock.calls).toEqual([["left"]]); 64 | expect(buttonsL.mock.calls).toEqual([["left"]]); 65 | 66 | r.trigger("keydown.left"); 67 | r.trigger("keydown.right"); 68 | r.trigger("click", { button: 0 }); 69 | r.trigger("click", { button: 2 }); 70 | expect(keysR).toHaveBeenCalledTimes(1); 71 | expect(keysR).toHaveBeenCalledWith("right"); 72 | expect(buttonsR).toHaveBeenCalledTimes(1); 73 | expect(buttonsR).toHaveBeenCalledWith("right"); 74 | }); 75 | it("prevent before", () => { 76 | const Comp = Vue.extend({ 77 | render(): VNode { 78 | return
; 79 | } 80 | }); 81 | const w = mount(Comp); 82 | const space = jest.fn(); 83 | const enter = jest.fn(); 84 | w.trigger("keydown.space", { 85 | preventDefault: space 86 | }); 87 | w.trigger("keydown.enter", { 88 | preventDefault: enter 89 | }); 90 | expect(space).toHaveBeenCalled(); 91 | expect(enter).toHaveBeenCalled(); 92 | }); 93 | it("prevent after", () => { 94 | const Comp = Vue.extend({ 95 | render(): VNode { 96 | return
; 97 | } 98 | }); 99 | const w = mount(Comp); 100 | const space = jest.fn(); 101 | const enter = jest.fn(); 102 | w.trigger("keydown.space", { 103 | preventDefault: space 104 | }); 105 | w.trigger("keydown.enter", { 106 | preventDefault: enter 107 | }); 108 | expect(space).toHaveBeenCalled(); 109 | expect(enter).not.toHaveBeenCalled(); 110 | }); 111 | it("stop before", () => { 112 | const propagated = jest.fn(); 113 | const spy = jest.fn(); 114 | const Comp = Vue.extend({ 115 | render(): VNode { 116 | return ( 117 |
propagated(keyNames[e.keyCode])}> 118 |
spy(keyNames[e.keyCode]))} /> 119 |
120 | ); 121 | } 122 | }); 123 | const w = mount(Comp); 124 | const inner = w.find(".inner"); 125 | inner.trigger("keydown.up"); 126 | inner.trigger("keydown.enter"); 127 | inner.trigger("keydown.space"); 128 | expect(spy.mock.calls).toEqual([["enter"]]); 129 | // all keydown events are not propagated 130 | expect(propagated).not.toHaveBeenCalled(); 131 | }); 132 | it("stop after", () => { 133 | const propagated = jest.fn(); 134 | const Comp = Vue.extend({ 135 | render(): VNode { 136 | return ( 137 |
propagated(keyNames[e.keyCode])}> 138 |
139 |
140 | ); 141 | } 142 | }); 143 | const w = mount(Comp); 144 | const inner = w.find(".inner"); 145 | inner.trigger("keydown.up"); 146 | inner.trigger("keydown.enter"); 147 | inner.trigger("keydown.space"); 148 | // keydown.enter is not propagated 149 | expect(propagated.mock.calls).toEqual([["up"], ["space"]]); 150 | }); 151 | it("exact", () => { 152 | const spy = jest.fn(); 153 | const Comp = Vue.extend({ 154 | render(): VNode { 155 | return
spy(keyNames[e.keyCode]))} />; 156 | } 157 | }); 158 | const w = mount(Comp); 159 | w.trigger("keydown.up", { 160 | ctrlKey: true 161 | }); 162 | w.trigger("keydown.down", { 163 | altKey: true 164 | }); 165 | w.trigger("keydown.left", { 166 | ctrlKey: true, 167 | altKey: true 168 | }); 169 | w.trigger("keydown.right", { 170 | ctrlKey: true, 171 | shiftKey: true, 172 | altKey: true 173 | }); 174 | expect(spy.mock.calls).toEqual([["left"]]); 175 | }); 176 | }); 177 | -------------------------------------------------------------------------------- /test/jest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "es6", 5 | "module": "es2015", 6 | "moduleResolution": "node", 7 | "outDir": "./temp", 8 | "jsx": "preserve", 9 | "jsxFactory": "VueTsxSupport", 10 | "strict": true, 11 | "noUnusedParameters": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "allowSyntheticDefaultImports": true, 15 | "inlineSourceMap": true, 16 | "paths": { 17 | "vue-tsx-support": ["../.."], 18 | "vue-tsx-support/*": ["../../*"], 19 | }, 20 | "experimentalDecorators": true 21 | }, 22 | "include": [ 23 | "**/*.ts", 24 | "**/*.tsx", 25 | "../../enable-check.d.ts" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /test/tsc/allow-element-unknown-attrs/test.tsx: -------------------------------------------------------------------------------- 1 | import * as vuetsx from "vue-tsx-support"; 2 | import Vue from "vue"; 3 | 4 | const noop = () => {}; 5 | 6 | /* 7 | * Intrinsic elements 8 | */ 9 | function intrinsicElements() { 10 | // OK 11 |
; 12 | // OK: kebab-case attribute have not to be defined 13 | ; 14 | // OK: unknown element 15 | ; 16 | 17 | // OK: unknown attrs are allowed 18 | ; 19 | // @ts-expect-error 20 |
; //// TS2322 | TS2326: /Type '(0|number)' is not assignable to/ 21 | // OK: unknown attrs are allowed 22 |
; 23 | // OK: unknown attrs are allowed 24 | ; 25 | } 26 | 27 | /* 28 | * Components written by standard way 29 | */ 30 | function standardComponent() { 31 | const MyComponent = Vue.extend({ 32 | props: ["a"], 33 | template: "{{ a }}" 34 | }); 35 | 36 | // Component unknown props are still rejected 37 | 38 | // NG: prop 39 | // @ts-expect-error: a does not exist 40 | ; 41 | 42 | // NG: HTML element 43 | // @ts-expect-error: accesskey does not exist 44 | ; //// TS2322 | TS2339 | TS2769: Property 'accesskey' does not exist 45 | 46 | // NG: native event handler 47 | // @ts-expect-error: nativeOnClick does not exist 48 | ; 49 | } 50 | -------------------------------------------------------------------------------- /test/tsc/allow-element-unknown-attrs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../base", 3 | "include": [ 4 | "*.tsx", 5 | "../../../enable-check.d.ts", 6 | "../../../options/allow-element-unknown-attrs.d.ts" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/tsc/allow-unknown-props/test.tsx: -------------------------------------------------------------------------------- 1 | import * as vuetsx from "vue-tsx-support"; 2 | import Vue from "vue"; 3 | 4 | const noop = () => {}; 5 | 6 | /* 7 | * Intrinsic elements 8 | */ 9 | function intrinsicElements() { 10 | // unknown attributes of intrinsic element are rejected 11 | 12 | // @ts-expect-error: domPropInnerHTML does not exist 13 | ; 14 | // @ts-expect-error: href does not exist 15 |
; 16 | } 17 | 18 | /* 19 | * Components written by standard way 20 | */ 21 | function standardComponent() { 22 | const MyComponent = Vue.extend({ 23 | props: ["a"], 24 | template: "{{ a }}" 25 | }); 26 | 27 | // OK 28 | ; 29 | // OK 30 | ; 31 | // OK 32 | ; 33 | } 34 | 35 | /* 36 | * ofType and convert 37 | */ 38 | function convert() { 39 | const MyComponent = vuetsx.ofType<{ a: string }>().convert(Vue.extend({})); 40 | 41 | // OK 42 | ; 43 | // OK 44 | ; 45 | // OK 46 | ; 47 | // NG 48 | // @ts-expect-error: property 'a' is missing 49 | ; 50 | // NG 51 | // @ts-expect-error: property 'a' must be string 52 | ; 53 | } 54 | -------------------------------------------------------------------------------- /test/tsc/allow-unknown-props/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../base", 3 | "include": [ 4 | "*.tsx", 5 | "../../../enable-check.d.ts", 6 | "../../../options/allow-unknown-props.d.ts" 7 | ] 8 | } 9 | 10 | -------------------------------------------------------------------------------- /test/tsc/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "es5", 5 | "jsx": "preserve", 6 | "jsxFactory": "VueTsxSupport", 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "incremental": true, 10 | "strict": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "allowSyntheticDefaultImports": true, 14 | "experimentalDecorators": true, 15 | "paths": { 16 | "vue-tsx-support": ["../.."], 17 | "vue-tsx-support/*": ["../../*"] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/tsc/basic/builtin-components.tsx: -------------------------------------------------------------------------------- 1 | ; 2 | 3 | ; 4 | 5 | done()} />; 6 | 7 | // @ts-expect-error: '(1|number)' is not assignable 8 | ; 9 | 10 | ; 11 | 12 | ; 13 | 14 | done()} />; 15 | 16 | // @ts-expect-error: '(1|number)' is not assignable 17 | ; 18 | 19 | ; 20 | 21 | ; 22 | 23 | ; 24 | 25 | ; 26 | 27 | // @ts-expect-error: 'number' is not assignable 28 | ; 29 | -------------------------------------------------------------------------------- /test/tsc/basic/classComponent.tsx: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | import { Component, Prop } from "vue-property-decorator"; 3 | import { 4 | InnerScopedSlots, 5 | DeclareOn, 6 | DeclareOnEvents, 7 | TsxTypeInfoOf, 8 | emit, 9 | emitOn, 10 | PickProps, 11 | DeclareProps, 12 | PickOwnProps, 13 | MakeOptional, 14 | AutoProps 15 | } from "vue-tsx-support"; 16 | 17 | @Component 18 | class Test extends Vue { 19 | _tsx!: DeclareProps, "baz">> & 20 | DeclareOn<{ e1: string; e2: (p1: string, p2: number) => void }> & 21 | DeclareOnEvents<{ onE1: string; onE2: (p1: string, p2: number) => void }>; 22 | 23 | @Prop(String) foo!: string; 24 | @Prop(Number) bar?: number; 25 | @Prop({ type: String, default: "defaultValue" }) 26 | baz!: string; 27 | 28 | bra!: number; 29 | 30 | $scopedSlots!: InnerScopedSlots<{ 31 | default: { ssprops: string }; 32 | optional?: string; 33 | }>; 34 | 35 | emitEvents() { 36 | emit(this, "e1", "value"); 37 | // @ts-expect-error: '(number | 1)' is not assignable 38 | emit(this, "e1", 1); 39 | 40 | emit(this, "e2", "value", 1); 41 | // @ts-expect-error: Expected 4 arguments 42 | emit(this, "e2", "value"); 43 | } 44 | 45 | emitOnEvents() { 46 | emitOn(this, "onE1", "value"); 47 | // @ts-expect-error: Expected 4 arguments 48 | emitOn(this, "onE1", 1); 49 | 50 | emitOn(this, "onE2", "value", 1); 51 | // @ts-expect-error: Expected 4 arguments 52 | emitOn(this, "onE2", "value"); 53 | } 54 | } 55 | 56 | class Test2 extends Test { 57 | piyo!: string[]; 58 | _tsx!: TsxTypeInfoOf & 59 | DeclareProps> & 60 | DeclareOn<{ e3: () => void }>; 61 | $scopedSlots!: Test["$scopedSlots"] & 62 | InnerScopedSlots<{ additional: { foo: string; bar: number } }>; 63 | 64 | emitEvents2() { 65 | emit(this, "e1", "value"); 66 | // @ts-expect-error: Expected 4 arguments 67 | emit(this, "e1", 1); 68 | 69 | emit(this, "e2", "value", 1); 70 | // @ts-expect-error: Expected 4 arguments 71 | emit(this, "e2", "value"); 72 | 73 | emit(this, "e3"); 74 | // @ts-expect-error: Expected 2 arguments 75 | emit(this, "e3", "value"); 76 | } 77 | } 78 | 79 | // OK 80 | ; 81 | // OK 82 | ; 83 | // OK 84 | ; 85 | // OK 86 | ; 87 | // OK 88 | console.log(p.toLocaleLowerCase()), 92 | e2: (p1, p2) => console.log(p1.toLocaleLowerCase(), p2.toFixed()) 93 | }} 94 | />; 95 | // @ts-expect-error: 'bra' does not exist 96 | ; 97 | // @ts-expect-error: 'foo' is missing 98 | ; 99 | 100 | console.log(p) }} 104 | />; 105 | 106 | // OK 107 | console.log(p) 111 | }} 112 | />; 113 | 114 | props.ssprops 118 | }} 119 | />; 120 | 121 | props.ssprops, 125 | optional: props => props.toUpperCase() 126 | }} 127 | />; 128 | 129 | props.toUpperCase() 134 | }} 135 | />; 136 | 137 | // OK 138 | ; 139 | // OK 140 | ; 141 | // OK 142 | console.log(p.toLocaleLowerCase()), 147 | e2: () => console.log("baz") 148 | }} 149 | />; 150 | // OK 151 | console.log(p.toLocaleLowerCase())] 156 | }} 157 | />; 158 | 159 | // @ts-expect-error: 'foo' is missing 160 | ; //// TS2322 | TS2326 | TS2769: 'foo' is missing 161 | // OK 162 | props.ssprops, 168 | additional: props => `${props.foo} ${props.bar}` 169 | }} 170 | />; 171 | 172 | @Component 173 | class GenericTest extends Vue { 174 | _tsx!: DeclareProps, "foo" | "bar">>; 175 | 176 | @Prop() foo!: T; 177 | @Prop(Function) bar!: (value: T) => string; 178 | 179 | $scopedSlots!: InnerScopedSlots<{ 180 | default: { item: T }; 181 | optional?: string; 182 | }>; 183 | } 184 | 185 | @Component 186 | class GenericParent extends Vue { 187 | value!: T; 188 | bar(value: T): string { 189 | return ""; 190 | } 191 | render(): VNode { 192 | const GenericTestT = GenericTest as new () => GenericTest; 193 | return ( 194 |
{this.bar(props.item)}
199 | }} 200 | /> 201 | ); 202 | } 203 | } 204 | 205 | @Component 206 | class Test3 extends Vue { 207 | _tsx!: DeclareProps, "bra" | "test">>; 208 | 209 | @Prop(String) foo!: string; 210 | @Prop(Number) bar?: number; 211 | 212 | bra!: number; 213 | 214 | test() {} 215 | } 216 | 217 | // OK 218 | ; 219 | // OK 220 | ; 221 | // @ts-expect-error: 'foo' is missing 222 | ; 223 | // OK 224 | // @ts-expect-error: 'bra' does not exist 225 | ; 226 | -------------------------------------------------------------------------------- /test/tsc/basic/componentFactory.tsx: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | import * as tsx from "vue-tsx-support"; 3 | 4 | function standardComponent() { 5 | const MyComponent = tsx.component( 6 | { 7 | props: { 8 | foo: String, 9 | bar: Number, 10 | baz: String 11 | }, 12 | render(): VNode { 13 | return {this.foo}; 14 | } 15 | }, 16 | /* requiredPropNames */ ["foo", "bar"] 17 | ); 18 | 19 | /* OK */ 20 | ; 21 | // baz is optional 22 | ; 23 | // other known attributes 24 | ; 25 | 26 | // @ts-expect-error 27 | ; 28 | // @ts-expect-error: 'bar' is missing 29 | ; 30 | ; 35 | ; 40 | ; 46 | ; 52 | } 53 | 54 | function standardComponentWithDataAndWatch() { 55 | const MyComponent = tsx.component({ 56 | props: { 57 | foo: String 58 | }, 59 | data() { 60 | return { a: "a" }; 61 | }, 62 | watch: { 63 | a(value: string) { 64 | console.log(value); 65 | } 66 | }, 67 | render(): VNode { 68 | return {this.foo + this.a}; 69 | } 70 | }); 71 | } 72 | 73 | function functionalComponent() { 74 | const MyComponent = tsx.component( 75 | { 76 | functional: true, 77 | props: { 78 | foo: String, 79 | bar: Number, 80 | baz: String 81 | }, 82 | render(_h, ctx): VNode { 83 | return {ctx.props.foo}; 84 | } 85 | }, 86 | ["foo", "bar"] 87 | ); 88 | /* OK */ 89 | ; 90 | // baz is optional 91 | ; 92 | // other known attributes 93 | ; 94 | 95 | /* NG */ 96 | // @ts-expect-error 97 | ; 98 | // @ts-expect-error: 'bar' is missing 99 | ; 100 | // @ts-expect-error: '(0|number)' is not assignable 101 | ; 102 | // @ts-expect-error: '("bar"|string)' is not assignable' 103 | ; 104 | // @ts-expect-error: '(1|number)' is not assignable 105 | ; 106 | // @ts-expect-error: 'unknown' does not exist 107 | ; 108 | } 109 | 110 | function withoutRequiredPropNames() { 111 | const MyComponent = tsx.componentFactory.create({ 112 | props: { 113 | foo: String, 114 | bar: Number, 115 | baz: String 116 | }, 117 | render(): VNode { 118 | return {this.foo}; 119 | } 120 | }); 121 | 122 | /* OK */ 123 | ; 124 | // foo, bar, baz are all optional 125 | ; 126 | // other known attributes 127 | ; 128 | 129 | // @ts-expect-error: '(0|number)' is not assignable 130 | ; 131 | // @ts-expect-error: '("bar"|string)' is not assignable 132 | ; 133 | // @ts-expect-error: '(1|number)' is not assignable 134 | ; 135 | // @ts-expect-error: 'unknown' does not exist 136 | ; 137 | } 138 | 139 | function inferRequiredPropNames() { 140 | const MyComponent = tsx.componentFactory.create({ 141 | props: { 142 | foo: { type: String, required: true as true }, 143 | bar: { type: Number, required: false }, 144 | baz: String 145 | }, 146 | render(): VNode { 147 | return {this.foo}; 148 | } 149 | }); 150 | 151 | /* OK */ 152 | ; 153 | // foo is required, bar and baz are optional 154 | ; 155 | // other known attributes 156 | ; 157 | 158 | /* NG */ 159 | // @ts-expect-error: 'foo' is missing 160 | ; 161 | ; 165 | ; 170 | ; 175 | ; 180 | } 181 | 182 | function withWrongRequiredPropNames() { 183 | // @ts-expect-error 184 | const MyComponent = tsx.component( 185 | { 186 | props: { foo: String } 187 | }, 188 | ["foo", "unknown"] 189 | ); 190 | } 191 | 192 | function componentFactoryOf() { 193 | const factory = tsx.componentFactoryOf< 194 | { onChange: number | string; onOk(target: any, id: string): void }, 195 | { content: string } 196 | >(); 197 | const MyComponent = factory.create({ 198 | props: { 199 | foo: String, 200 | bar: Number 201 | }, 202 | computed: { 203 | okNode(): VNode { 204 | return
{this.$scopedSlots.content("foo")}
; 205 | }, 206 | ngNode(): VNode { 207 | return ( 208 |
209 | { 210 | // @ts-expect-error: ('0'|number) is not assignable 211 | this.$scopedSlots.content(0) 212 | } 213 |
214 | ); 215 | } 216 | }, 217 | render(): VNode { 218 | return
{[this.okNode, this.ngNode]}
; 219 | } 220 | }); 221 | 222 | /* checking type of scopedSlots */ 223 | p }} />; 224 | p.toString() }} 227 | />; 228 | ; 232 | 233 | /* checking type of custom event handler */ 234 | {}} />; 235 | {}} />; 236 | {}} 239 | />; 240 | {}} 243 | />; 244 | { 246 | console.log(id); 247 | }} 248 | />; 249 | } 250 | 251 | function optionalScopedSlot() { 252 | const factory = tsx.componentFactoryOf<{}, { required: string; optional?: number }>(); 253 | const MyComponent = factory.create({ 254 | props: { 255 | foo: String, 256 | bar: Number 257 | }, 258 | render(): VNode { 259 | return ( 260 |
261 | {this.$scopedSlots.optional!(1)} 262 | { 263 | // @ts-expect-error: possibly 'undefined' 264 | this.$scopedSlots.optional(1) 265 | } 266 |
267 | ); 268 | } 269 | }); 270 | 271 | /* checking type of scopedSlots */ 272 | p.toUpperCase() }} />; 273 | p.toUpperCase(), optional: p => p.toString() }} />; 274 | p.toString() }} 277 | />; 278 | } 279 | 280 | function extendFrom() { 281 | /* 282 | * extend from tsx component 283 | */ 284 | const Base1 = tsx.component( 285 | { 286 | props: { foo: String }, 287 | computed: { 288 | fooUpper(): string { 289 | return this.foo.toUpperCase(); 290 | } 291 | } 292 | }, 293 | ["foo"] 294 | ); 295 | 296 | const Ext1 = tsx.extendFrom(Base1).create({ 297 | props: { bar: String }, 298 | render(): VNode { 299 | return
{this.fooUpper + this.bar}
; 300 | } 301 | }); 302 | 303 | ; 304 | // @ts-expect-error: 'foo' is missing 305 | ; 306 | ; 312 | 313 | /* 314 | * extend from class base tsx component 315 | */ 316 | class Base2 extends tsx.Component<{ foo: string }, { onOk: string }> { 317 | get fooUpper() { 318 | return this.$props.foo.toUpperCase(); 319 | } 320 | } 321 | 322 | const Ext2 = tsx.extendFrom(Base2).create({ 323 | props: { bar: String }, 324 | render(): VNode { 325 | return
{this.fooUpper + this.bar}
; 326 | } 327 | }); 328 | 329 | ; 330 | // @ts-expect-error: 'foo' is missing 331 | ; 332 | ; 338 | 339 | /* 340 | * extend from standard component 341 | */ 342 | const Base3 = Vue.extend({ 343 | data() { 344 | return { foo: "fooValue" }; 345 | } 346 | }); 347 | 348 | const Ext3 = tsx.extendFrom(Base3).create({ 349 | props: { 350 | bar: String 351 | }, 352 | render(): VNode { 353 | return {this.foo + this.bar}; 354 | } 355 | }); 356 | 357 | ; 358 | ; 359 | ; 364 | 365 | /* 366 | * extend from standard class base component 367 | */ 368 | class Base4 extends Vue { 369 | get foo() { 370 | return "fooValue"; 371 | } 372 | } 373 | 374 | const Ext4 = tsx.extendFrom(Base4).create({ 375 | props: { 376 | bar: String 377 | }, 378 | render(): VNode { 379 | return {this.foo + this.bar}; 380 | } 381 | }); 382 | 383 | ; 384 | ; 385 | ; 390 | } 391 | 392 | function withXXX() { 393 | const Base = tsx 394 | .componentFactoryOf<{ onOk: { value: string } }, { default: { value: string } }>() 395 | .create({ 396 | props: { foo: { type: String, required: true as true } } 397 | }); 398 | const Ext = tsx.withNativeOn(Base); 399 | ; 400 | console.log(v.value)} />; 401 | v.value }} />; 402 | {}} />; 403 | // @ts-expect-error: 'foo' is missing 404 | ; 405 | } 406 | 407 | function emitHelper() { 408 | const Component = tsx.componentFactoryOf<{ onOk: string | number }>().create({ 409 | props: { foo: { type: String, required: true } }, 410 | methods: { 411 | emitOk() { 412 | tsx.emitOn(this, "onOk", "foo"); 413 | tsx.emitOn(this, "onOk", 1); 414 | // @ts-expect-error 415 | tsx.emitOn(this, "onOk", true); 416 | // @ts-expect-error 417 | tsx.emitOn(this, "onNg", { value: "foo" }); 418 | }, 419 | updateFoo() { 420 | tsx.emitUpdate(this, "foo", "value"); 421 | // @ts-expect-error 422 | tsx.emitUpdate(this, "fooo", "value"); 423 | // @ts-expect-error 424 | tsx.emitUpdate(this, "foo", 0); 425 | } 426 | } 427 | }); 428 | const Component2 = tsx.component({ 429 | props: { foo: { type: String, required: true } }, 430 | methods: { 431 | updateFoo() { 432 | tsx.emitUpdate(this, "foo", "value"); 433 | // @ts-expect-error 434 | tsx.emitUpdate(this, "fooo", "value"); 435 | // @ts-expect-error 436 | tsx.emitUpdate(this, "foo", 0); 437 | } 438 | } 439 | }); 440 | } 441 | -------------------------------------------------------------------------------- /test/tsc/basic/extend.tsx: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import * as vuetsx from "vue-tsx-support"; 3 | 4 | const noop = () => {}; 5 | 6 | interface Props { 7 | foo: string; 8 | } 9 | 10 | interface Events { 11 | onOk: void; 12 | } 13 | 14 | interface Props2 { 15 | bar: string; 16 | } 17 | 18 | interface Events2 { 19 | onErr: string; 20 | } 21 | 22 | const a = { foo: 1, bar: "" }; 23 | 24 | function by_createComponent() { 25 | const Base = vuetsx.createComponent({}); 26 | 27 | // NG 28 | // @ts-expect-error: 'foo' is missing 29 | ; 30 | // OK 31 | ; 32 | // NG 33 | ; 38 | ; 43 | 44 | /* add more attributes */ 45 | const Extend = vuetsx.ofType().extendFrom(Base); 46 | // OK 47 | console.log(s)} />; 48 | // @ts-expect-error: 'bar' is missing 49 | ; 50 | // @ts-expect-error: 'foo' is missing 51 | ; 52 | 53 | const WithNativeOn = vuetsx.withNativeOn(Base); 54 | ; 55 | 56 | const WithHtmlAttrs = vuetsx.withHtmlAttrs(Base); 57 | ; 58 | 59 | const WithUnknownProps = vuetsx.withUnknownProps(Base); 60 | ; 61 | } 62 | 63 | function by_convert() { 64 | const Base = vuetsx.ofType().convert(Vue.extend({})); 65 | const BaseWithStatic: typeof Base & { staticMember: number } = Base as any; 66 | BaseWithStatic.staticMember = 1; 67 | 68 | /* add more attributes */ 69 | const Extend = vuetsx.ofType().extendFrom(BaseWithStatic); 70 | // OK 71 | console.log(s)} />; 72 | // OK 73 | console.log(Extend.staticMember); 74 | // @ts-expect-error: 'bar' is missing 75 | ; //// TS2322 | TS2326 | TS2769: 'bar' is missing 76 | // @ts-expect-error: 'foo' is missing 77 | ; 78 | 79 | const WithNativeOn = vuetsx.withNativeOn(Base); 80 | ; 81 | 82 | const WithHtmlAttrs = vuetsx.withHtmlAttrs(Base); 83 | ; 84 | 85 | const WithUnknownProps = vuetsx.withUnknownProps(Base); 86 | ; 87 | } 88 | 89 | function by_class() { 90 | class Base extends vuetsx.Component { 91 | someProp: string = "foo"; 92 | someMethod() {} 93 | } 94 | 95 | /* add more attributes */ 96 | const Extend = vuetsx.ofType().extendFrom(Base); 97 | // OK 98 | console.log(s)} />; 99 | // NG 100 | // @ts-expect-error: 'bar' is missing 101 | ; 102 | // NG 103 | // @ts-expect-error: 'foo' is missing 104 | ; 105 | 106 | // Extend inherits prototype of Base. 107 | const ext = new Extend(); 108 | console.log(ext.someProp, ext.someMethod()); 109 | 110 | const WithNativeOn = vuetsx.withNativeOn(Base); 111 | ; 112 | console.log(new WithNativeOn().someProp); 113 | 114 | const WithHtmlAttrs = vuetsx.withHtmlAttrs(Base); 115 | ; 116 | console.log(new WithHtmlAttrs().someProp); 117 | 118 | const WithUnknownProps = vuetsx.withUnknownProps(Base); 119 | ; 120 | console.log(new WithUnknownProps().someProp); 121 | } 122 | 123 | function by_componentFactory() { 124 | const Base = vuetsx.componentFactoryOf().create( 125 | { 126 | props: { 127 | foo: String 128 | }, 129 | computed: { 130 | someProp(): string { 131 | return ""; 132 | } 133 | }, 134 | methods: { 135 | someMethod() {} 136 | } 137 | }, 138 | ["foo"] 139 | ); 140 | 141 | /* add more attributes */ 142 | const Extend = vuetsx.ofType().extendFrom(Base); 143 | // OK 144 | console.log(s)} />; 145 | // NG 146 | // @ts-expect-error: 'bar' is missing 147 | ; 148 | // NG 149 | // @ts-expect-error: 'foo' is missing 150 | ; 151 | 152 | // Extend inherits prototype of Base. 153 | const ext = new Extend(); 154 | console.log(ext.someProp, ext.someMethod()); 155 | 156 | const WithNativeOn = vuetsx.withNativeOn(Base); 157 | ; 158 | console.log(new WithNativeOn().someProp); 159 | 160 | const WithHtmlAttrs = vuetsx.withHtmlAttrs(Base); 161 | ; 162 | console.log(new WithHtmlAttrs().someProp); 163 | 164 | const WithUnknownProps = vuetsx.withUnknownProps(Base); 165 | ; 166 | console.log(new WithUnknownProps().someProp); 167 | } 168 | -------------------------------------------------------------------------------- /test/tsc/basic/mixin.tsx: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | import * as vuetsx from "vue-tsx-support"; 3 | 4 | const noop = () => {}; 5 | 6 | function basicFunctionary() { 7 | const factory = vuetsx.componentFactory 8 | .mixin({ 9 | props: { foo: String }, 10 | computed: { 11 | fooUpper(): string { 12 | return this.foo.toUpperCase(); 13 | } 14 | } 15 | }) 16 | .mixin( 17 | vuetsx.component( 18 | { 19 | props: { bar: String }, 20 | computed: { 21 | barUpper(): string { 22 | return this.bar.toUpperCase(); 23 | } 24 | } 25 | }, 26 | ["bar"] 27 | ) 28 | ) 29 | .mixin( 30 | Vue.extend({ 31 | data() { 32 | return { baz: "piyo" }; 33 | } 34 | }) 35 | ); 36 | 37 | const Component = factory.create({ 38 | props: { bra: Number }, 39 | render(): VNode { 40 | return
{this.fooUpper + this.barUpper + this.baz}
; 41 | } 42 | }); 43 | 44 | ; 45 | // @ts-expect-error: 'bar' is missing 46 | ; 47 | } 48 | -------------------------------------------------------------------------------- /test/tsc/basic/modifiers.tsx: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | import { modifiers as m } from "vue-tsx-support"; 3 | 4 | function modifiers() { 5 |
; 6 |
{})} />; 7 |
; 8 |
console.log(e.keyCode))} />; 9 |
console.log(e.keyCode))} />; 10 |
; 11 |
; 12 |
; 13 |
; 14 |
console.log(e.keyCode))} />; 15 |
; 16 |
console.log(e.keyCode))} />; 17 | 18 | // each modifiers can be specified only once 19 |
; 25 |
; 31 |
; 37 |
; 43 | 44 | // key modifier can be specified only once 45 |
; 51 |
; 56 |
; 62 | 63 | // mouse button modifier can be specified only once 64 |
; 70 |
; 76 | 77 | // key modifier and mouse button modifier can't be specified together 78 |
; 84 |
; 90 | 91 | // xxx and noxxx can't be specified together 92 |
; 98 |
; 104 |
; 110 |
; 116 |
; 122 |
; 128 |
; 134 |
; 140 | // 'exact' and other modkey can't be specified together 141 |
; 147 |
; 152 |
; 157 | } 158 | -------------------------------------------------------------------------------- /test/tsc/basic/propsObject.tsx: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | import * as tsx from "vue-tsx-support"; 3 | 4 | function standardComponent() { 5 | const MyComponent = tsx.component({ 6 | props: { 7 | foo: { type: String, required: true as true }, 8 | bar: Number 9 | }, 10 | render(): VNode { 11 | return {this.foo}; 12 | } 13 | }); 14 | 15 | /* OK */ 16 | ; 17 | // bar is optional 18 | ; 19 | // @ts-expect-error: 'foo' is missing 20 | ; 21 | 22 | const MyComponent2 = tsx.withPropsObject(MyComponent); 23 | /* OK */ 24 | ; 25 | ; 26 | ; 27 | ; 28 | // @ts-expect-error: 'foo' is missing 29 | ; 30 | } 31 | -------------------------------------------------------------------------------- /test/tsc/basic/register-global-component.ts: -------------------------------------------------------------------------------- 1 | import * as vuetsx from "vue-tsx-support"; 2 | import Vue from "vue"; 3 | 4 | interface Props { 5 | text: string; 6 | } 7 | 8 | const MyComponent = vuetsx.createComponent({}); 9 | Vue.component("my-component", MyComponent); 10 | -------------------------------------------------------------------------------- /test/tsc/basic/test.tsx: -------------------------------------------------------------------------------- 1 | import * as vuetsx from "vue-tsx-support"; 2 | import component from "vue-class-component"; 3 | import Vue from "vue"; 4 | 5 | const noop = () => {}; 6 | 7 | function assertType(value: T) { 8 | return value; 9 | } 10 | 11 | /* 12 | * Intrinsic elements 13 | */ 14 | function intrinsicElements() { 15 | // OK 16 |
; 17 | // OK 18 |
; 19 | // OK 20 |
; 21 | // OK 22 | ; 23 | // OK 24 | ; 25 | // OK 26 | ; 27 | // OK 28 | ; 29 | // OK: kebab-case attribute have not to be defined 30 | ; 31 | // OK: unknown element 32 | ; 33 | 34 | // OK: specify style in 3 patterns 35 |
; 36 |
; 37 |
; 38 | 39 | // OK: inplace event handler: type of e.target is determined by containing tag. 40 | { 42 | assertType(e); 43 | assertType(e.target); 44 | }} 45 | />; 46 | 47 | const onInput = (e: InputEvent) => {}; 48 | ; 49 | 50 | // @ts-expect-error 51 |
; 52 | // @ts-expect-error 53 |
; 54 | // @ts-expect-error 55 | ; 56 | } 57 | 58 | /* 59 | * Components written by standard way 60 | */ 61 | function standardComponent() { 62 | const MyComponent = Vue.extend({ 63 | props: ["a"], 64 | template: "{{ a }}" 65 | }); 66 | 67 | // OK 68 | ; 69 | // OK 70 | ; 71 | // OK 72 | ; 73 | // OK 74 | ; 75 | // OK: unknown attributes are allowed in JSX spread. 76 | ; 77 | 78 | // @ts-expect-error 79 | ; 80 | 81 | // @ts-expect-error 82 | ; 83 | 84 | // NG: native event handler 85 | // @ts-expect-error 86 | ; 87 | 88 | // OK: specify style in 3 patterns 89 | ; 90 | ; 91 | ; 92 | } 93 | 94 | interface Props { 95 | a: string; 96 | b?: number; 97 | } 98 | 99 | interface Events { 100 | onChange: string; 101 | } 102 | 103 | interface ScopedSlots { 104 | default: { ssprops: string }; 105 | } 106 | 107 | /* 108 | * ofType and convert 109 | */ 110 | function convert() { 111 | const MyComponent1 = vuetsx.ofType().convert( 112 | Vue.extend({ 113 | methods: { greet() {} } 114 | }) 115 | ); 116 | const MyComponent2 = vuetsx.ofType().convert(Vue.extend({})); 117 | const MyComponent3 = vuetsx.ofType().convert({} as any); 118 | 119 | // NG: `a` is required 120 | // @ts-expect-error: 'a' is missing 121 | ; 122 | 123 | let vm!: InstanceType; 124 | vm.greet(); // OK 125 | 126 | // OK 127 | ; 128 | // OK 129 | console.log(value.toUpperCase())} />; 130 | // NG: `c` is not defined 131 | ; 136 | // NG: `a` must be string 137 | ; 141 | // NG: `b` must be number 142 | ; 147 | 148 | // OK 149 | props.ssprops }} />; 150 | // NG 151 | ; 156 | // NG 157 | 161 | // @ts-expect-error: 'xxx' does not exist 162 | props.xxx 163 | }} 164 | />; 165 | 166 | // NG: `a` is required 167 | // @ts-expect-error 168 | ; 169 | 170 | // OK 171 | ; 172 | // OK 173 | console.log(value.toUpperCase())} />; 174 | // NG: `c` is not defined 175 | ; 180 | // NG: `a` must be string 181 | ; 185 | // NG: `b` must be number 186 | ; 191 | 192 | // NG: props object does not allow by default 193 | // @ts-expect-error 194 | ; 195 | } 196 | 197 | /* 198 | * createComponent 199 | */ 200 | function createComponent() { 201 | const MyComponent = vuetsx.createComponent({}); 202 | 203 | // NG: `a` is required 204 | // @ts-expect-error 205 | ; 206 | // OK 207 | ; 208 | // OK 209 | ; 210 | // NG: `c` is not defined 211 | ; 216 | // NG: `a` must be string 217 | ; 221 | // NG: `b` must be number 222 | ; 227 | } 228 | 229 | /* 230 | * vue-class-component 231 | */ 232 | function vueClassComponent() { 233 | @component 234 | class MyComponent extends vuetsx.Component {} 235 | 236 | @component 237 | class MyComponent2 extends vuetsx.Component { 238 | render() { 239 | return
{this.$scopedSlots.default({ ssprops: "foo" })}
; 240 | } 241 | } 242 | 243 | @component 244 | class MyComponent3 extends vuetsx.Component { 245 | render() { 246 | return ( 247 |
248 | {this.$scopedSlots.default({ 249 | // @ts-expect-error: 'number' is not assignable 250 | ssprops: 1 251 | })} 252 |
253 | ); 254 | } 255 | } 256 | 257 | // NG: `a` is required 258 | // @ts-expect-error 259 | ; 260 | 261 | // OK 262 | ; 263 | // OK 264 | p.xxx }} />; 265 | // OK 266 | [{p.xxx}] }} />; 267 | 268 | // NG: `c` is not defined 269 | ; 274 | // NG: `a` must be string 275 | ; 279 | // NG: `b` must be number 280 | ; 285 | 286 | // OK 287 | p.ssprops }} />; 288 | // OK (unfortunately) 289 | ; 290 | // NG 291 | 295 | // @ts-expect-error: 'xxx' is not exist 296 | p.xxx 297 | }} 298 | />; 299 | } 300 | 301 | function knownAttrs() { 302 | const nope = () => {}; 303 | const MyComponent = vuetsx.component({}); 304 | 305 | ; 306 | ; 307 | ; 308 | } 309 | 310 | function functionalWrapper() { 311 | const MyComponent = vuetsx.component({}); 312 | const Parent = vuetsx.component({ 313 | functional: true, 314 | render(_h, { data, children }) { 315 | return {children}; 316 | } 317 | }); 318 | } 319 | -------------------------------------------------------------------------------- /test/tsc/basic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../base", 3 | "include": [ 4 | "*.ts", 5 | "*.tsx", 6 | "../../../enable-check.d.ts" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/tsc/basic/vca.tsx: -------------------------------------------------------------------------------- 1 | import { component, SetupContext, emit, emitOn, updateEmitter } from "vue-tsx-support/lib/vca"; 2 | import { ref } from "@vue/composition-api"; 3 | import { VNode } from "vue"; 4 | 5 | const nope = () => undefined; 6 | 7 | const MyComponent = component({ 8 | name: "MyComponentName", 9 | props: { 10 | foo: String, 11 | bar: { type: Boolean, required: true } 12 | }, 13 | setup(props, ctx) { 14 | const el = ref(null); 15 | 16 | const emitUpdate = updateEmitter(); 17 | emitUpdate(ctx, "foo", "value"); 18 | // @ts-expect-error 19 | emitUpdate(ctx, "fooo", "value"); //// TS2345 20 | // @ts-expect-error 21 | emitUpdate(ctx, "foo", 0); //// TS2345 22 | 23 | return () => ( 24 |
25 | {(ctx.slots.default || nope)()} 26 |
27 | ); 28 | } 29 | }); 30 | 31 | ; // OK 32 | ; // OK 33 | // @ts-expect-error 34 | ; 35 | 36 | const MyComponent2 = component({ 37 | props: { 38 | foo: String 39 | }, 40 | setup( 41 | props, 42 | ctx: SetupContext< 43 | { onCutstomEvent: string | number }, 44 | { ss: string | boolean }, 45 | { customEvent: string | number } 46 | > 47 | ) { 48 | const emitUpdate = updateEmitter(); 49 | emit(ctx, "customEvent", "value"); 50 | emitOn(ctx, "onCutstomEvent", "value"); 51 | emit(ctx, "customEvent", 1); 52 | emitOn(ctx, "onCutstomEvent", 1); 53 | 54 | // @ts-expect-error 55 | emit(ctx, "customEvent2", "value"); //// TS2345 56 | // @ts-expect-error 57 | emitOn(ctx, "onCutstomEvent2", "value"); //// TS2345 58 | 59 | // @ts-expect-error 60 | emit(ctx, "customEvent", true); //// TS2345 61 | // @ts-expect-error 62 | emitOn(ctx, "onCutstomEvent", true); //// TS2345 63 | 64 | emitUpdate(ctx, "foo", "value"); 65 | // @ts-expect-error 66 | emitUpdate(ctx, "fooo", "value"); //// TS2345 67 | // @ts-expect-error 68 | emitUpdate(ctx, "foo", 0); //// TS2345 69 | 70 | return () => ( 71 |
72 | {ctx.slots.ss(true)} 73 | {ctx.slots.ss("value")} 74 |
75 | ); 76 | } 77 | }); 78 | 79 | const MyComponent3 = component({ 80 | props: { 81 | foo: String 82 | }, 83 | setup(props, ctx: SetupContext<{ onCutstomEvent: string | number }, { ss: string | boolean }>) { 84 | const emitUpdate = updateEmitter(); 85 | 86 | emitOn(ctx, "onCutstomEvent", "value"); 87 | emitOn(ctx, "onCutstomEvent", 1); 88 | // @ts-expect-error 89 | emitOn(ctx, "onCutstomEvent2", "value"); //// TS2345 90 | // @ts-expect-error 91 | emitOn(ctx, "onCutstomEvent", true); //// TS2345 92 | 93 | emitUpdate(ctx, "foo", "value"); 94 | // @ts-expect-error 95 | emitUpdate(ctx, "fooo", "value"); //// TS2345 96 | // @ts-expect-error 97 | emitUpdate(ctx, "foo", 0); //// TS2345 98 | 99 | return () =>
; 100 | } 101 | }); 102 | 103 | 106 | console.log( 107 | // @ts-expect-error: 'toUpperCase' does not exist on type 'string | number' 108 | v.toUpperCase() 109 | ) 110 | } 111 | />; 112 | 113 | const MyComponentWithRender = component({ 114 | name: "MyComponentName", 115 | props: { 116 | foo: String, 117 | bar: { type: Boolean, required: true } 118 | }, 119 | setup(props, ctx) { 120 | const el = ref(null); 121 | const greet = () => console.log("hello"); 122 | const render_ = () => ( 123 |
124 | {(ctx.slots.default || nope)()} 125 |
126 | ); 127 | return { 128 | greet, 129 | render_ 130 | }; 131 | }, 132 | render(): VNode { 133 | return this.render_(); 134 | } 135 | }); 136 | 137 | const vm = {} as InstanceType; 138 | vm.greet(); 139 | -------------------------------------------------------------------------------- /test/tsc/declaration/test.tsx: -------------------------------------------------------------------------------- 1 | import * as vuetsx from "vue-tsx-support"; 2 | import Vue, { VNode, VNodeChildrenArrayContents, VNodeData, VueConstructor } from "vue"; 3 | 4 | // export component with --declaration 5 | 6 | export const Component = vuetsx.component({ 7 | name: "Component", 8 | props: { 9 | foo: String 10 | }, 11 | render(h): VNode { 12 | return
{this.foo}
; 13 | } 14 | }); 15 | 16 | export const FunctionalComponent = vuetsx.component({ 17 | functional: true, 18 | name: "Component2", 19 | props: { 20 | foo: String 21 | }, 22 | render(h, { props }): VNode { 23 | return
{props.foo}
; 24 | } 25 | }); 26 | 27 | export class ClassComponent extends vuetsx.Component< 28 | { foo: string }, 29 | { onClick: string }, 30 | { default: string } 31 | > {} 32 | -------------------------------------------------------------------------------- /test/tsc/declaration/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../base", 3 | "compilerOptions": { 4 | "declaration": true 5 | }, 6 | "include": [ 7 | "*.tsx", 8 | "../../../enable-check.d.ts", 9 | "../../../options/allow-element-unknown-attrs.d.ts", 10 | "../../../options/allow-unknown-props.d.ts", 11 | "../../../options/enable-html-attrs.d.ts", 12 | "../../../options/enable-nativeon.d.ts", 13 | "../../../options/enable-vue-router.d.ts" 14 | ] 15 | } 16 | 17 | -------------------------------------------------------------------------------- /test/tsc/enable-html-attrs/test.tsx: -------------------------------------------------------------------------------- 1 | import * as vuetsx from "vue-tsx-support"; 2 | import Vue from "vue"; 3 | 4 | const noop = () => {}; 5 | 6 | /* 7 | * Components written by standard way 8 | */ 9 | function standardComponent() { 10 | const MyComponent = Vue.extend({}); 11 | 12 | // NG 13 | ; 17 | // OK 18 | ; 19 | // NG 20 | ; 24 | } 25 | -------------------------------------------------------------------------------- /test/tsc/enable-html-attrs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../base", 3 | "include": [ 4 | "*.tsx", 5 | "../../../enable-check.d.ts", 6 | "../../../options/enable-html-attrs.d.ts" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/tsc/enable-nativeon/test.tsx: -------------------------------------------------------------------------------- 1 | import * as vuetsx from "vue-tsx-support"; 2 | import Vue from "vue"; 3 | 4 | const noop = () => {}; 5 | const func = (_: KeyboardEvent) => {}; 6 | 7 | /* 8 | * Components written by standard way 9 | */ 10 | function standardComponent() { 11 | const MyComponent = Vue.extend({}); 12 | 13 | // NG 14 | ; 18 | // NG 19 | ; 23 | // OK 24 | ; 25 | // OK 26 | ; 27 | // NG 28 | ; 32 | // NG 33 | ; 39 | } 40 | -------------------------------------------------------------------------------- /test/tsc/enable-nativeon/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../base", 3 | "include": [ 4 | "*.tsx", 5 | "../../../enable-check.d.ts", 6 | "../../../options/enable-nativeon.d.ts" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/tsc/enable-vuerouter/test.tsx: -------------------------------------------------------------------------------- 1 | import * as vuetsx from "vue-tsx-support"; 2 | import Vue from "vue"; 3 | 4 | const noop = () => {}; 5 | const func = (_: KeyboardEvent) => {}; 6 | 7 | /* 8 | * Components written by standard way 9 | */ 10 | function standardComponent() { 11 | const MyComponent = Vue.extend({}); 12 | 13 | ; 14 | ; 15 | // NG 16 | ; 22 | } 23 | -------------------------------------------------------------------------------- /test/tsc/enable-vuerouter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../base", 3 | "include": [ 4 | "*.tsx", 5 | "../../../enable-check.d.ts", 6 | "../../../options/enable-vue-router.d.ts" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/tsc/runner.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const glob = require("glob"); 3 | const { spawnSync } = require("child_process"); 4 | 5 | const projects = glob.sync(path.join(__dirname, "**/tsconfig.json")); 6 | 7 | const runAll = () => { 8 | let hasError = false; 9 | for (const p of projects) { 10 | console.info(`testing ${p} ...`); 11 | const outDir = path.join(path.dirname(p), ".temp"); 12 | const { status } = spawnSync("tsc", ["-p", p, "--outDir", outDir], { 13 | shell: true, 14 | stdio: "inherit" 15 | }); 16 | if (status !== 0) { 17 | hasError = true; 18 | } 19 | } 20 | return hasError ? 1 : 0; 21 | }; 22 | 23 | process.exit(runAll()); 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "lib": [ 6 | "dom", 7 | "es2015" 8 | ], 9 | "moduleResolution": "node", 10 | "declaration": true, 11 | "declarationDir": "./lib", 12 | "strict": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "allowSyntheticDefaultImports": true, 17 | "inlineSourceMap": true, 18 | "esModuleInterop": true 19 | }, 20 | "include": [ 21 | "src/**/*.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /types/base.d.ts: -------------------------------------------------------------------------------- 1 | import * as dom from "./dom"; 2 | import Vue, { 3 | VNode, 4 | VNodeData, 5 | VNodeChildrenArrayContents, 6 | ComponentOptions, 7 | VueConstructor 8 | } from "vue"; 9 | import { ScopedSlot } from "vue/types/vnode"; 10 | 11 | export interface ElementAdditionalAttrs { 12 | // extension point. 13 | } 14 | 15 | export type ScopedSlotReturnType = ReturnType; 16 | export type TypedScopedSlot

= (props: P) => ScopedSlotReturnType; 17 | 18 | export type KnownAttrs = { 19 | class?: VNodeData["class"]; 20 | staticClass?: VNodeData["staticClass"]; 21 | key?: VNodeData["key"]; 22 | ref?: VNodeData["ref"] | { value: unknown }; 23 | slot?: VNodeData["slot"]; 24 | style?: VNodeData["style"] | string; 25 | domProps?: VNodeData["domProps"]; 26 | attrs?: VNodeData["attrs"]; 27 | hook?: VNodeData["hook"]; 28 | on?: VNodeData["on"]; 29 | nativeOn?: VNodeData["nativeOn"]; 30 | id?: string; 31 | refInFor?: boolean; 32 | domPropsInnerHTML?: string; 33 | }; 34 | 35 | export type Arg1 = T extends (arg1: infer A1) => any | undefined ? A1 : never; 36 | 37 | export type InnerScopedSlotReturnType = Vue["$scopedSlots"] extends { 38 | [name: string]: ((...args: any[]) => infer T) | undefined; 39 | } 40 | ? T 41 | : never; 42 | export type InnerScopedSlot = (props: T) => InnerScopedSlotReturnType; 43 | export type InnerScopedSlots = { [K in keyof T]: InnerScopedSlot> }; 44 | 45 | export type ScopedSlotHandlers = { 46 | [K in keyof InnerSSType]: TypedScopedSlot>; 47 | }; 48 | 49 | export type EventHandler = [E] extends [(...args: any[]) => any] ? E : (payload: E) => void; 50 | export type EventHandlers = { [K in keyof E]?: EventHandler | EventHandler[] }; 51 | 52 | export type DeclareProps

= { props: P }; 53 | export type DeclareOnEvents = { onEvents: E }; 54 | export type DeclareOn = { events: E }; 55 | export type DeclareAttributes = { attributes: A }; 56 | 57 | export type TsxComponentTypeInfo = DeclareProps & 58 | DeclareOnEvents & 59 | DeclareOn & 60 | DeclareAttributes; 61 | 62 | export type TsxTypeInfoOf = V extends { _tsx: infer T } ? T : {}; 63 | 64 | export type PropsOf = TsxTypeInfoOf extends DeclareProps ? X : {}; 65 | export type PrefixedEventsOf = TsxTypeInfoOf extends DeclareOnEvents ? X : {}; 66 | export type OnOf = TsxTypeInfoOf extends DeclareOn ? X : {}; 67 | export type AttributesOf = TsxTypeInfoOf extends DeclareAttributes ? X : {}; 68 | export type IsPropsObjectAllowed = V extends { _tsx_allowPropsObject: true } ? true : false; 69 | 70 | type CombinedTsxComponentAttrsOtherThanProps = KnownAttrs & 71 | Attributes & 72 | EventHandlers & { 73 | on?: EventHandlers & VNodeData["on"]; 74 | scopedSlots?: ScopedSlotHandlers; 75 | }; 76 | 77 | type CombinedTsxComponentAttrs< 78 | Attributes, 79 | Props, 80 | PrefixedEvents, 81 | On, 82 | InnerSS, 83 | AllowPropsObject extends boolean 84 | > = 85 | | (AllowPropsObject extends true 86 | ? { props: Props } & Partial & 87 | CombinedTsxComponentAttrsOtherThanProps 88 | : never) 89 | | (Props & CombinedTsxComponentAttrsOtherThanProps); 90 | 91 | export type ElementAttrs = E[1] & 92 | KnownAttrs & 93 | EventHandlers> & { 94 | on?: EventHandlers>; 95 | } & ElementAdditionalAttrs; 96 | 97 | export interface Element extends VNode {} 98 | 99 | export interface ElementClass extends Vue {} 100 | 101 | export type IntrinsicElements = { 102 | [K in keyof dom.IntrinsicElementTypes]: ElementAttrs; 103 | }; 104 | 105 | type PropNameCandidates = Exclude< 106 | keyof V, 107 | keyof Parent | keyof ComponentOptions | "_tsx" 108 | >; 109 | 110 | export type PickProps> = Pick; 111 | export type PickOwnProps< 112 | V extends Parent, 113 | Parent extends Vue, 114 | Names extends PropNameCandidates 115 | > = Pick; 116 | export type AutoProps = Pick< 117 | V, 118 | PropNameCandidates 119 | >; 120 | export type MakeOptional = Omit & 121 | Partial>; 122 | -------------------------------------------------------------------------------- /types/builtin-components.d.ts: -------------------------------------------------------------------------------- 1 | export interface TransitionPropsBase { 2 | name?: string; 3 | appear?: boolean; 4 | css?: boolean; 5 | type?: string; 6 | enterClass?: string; 7 | leaveClass?: string; 8 | enterActiveClass?: string; 9 | leaveActiveClass?: string; 10 | appearClass?: string; 11 | appearActiveClass?: string; 12 | 13 | onBeforeEnter?: (el: Element) => void; 14 | onEnter?: (el: Element, done: () => void) => void; 15 | onAfterEnter?: (el: Element) => void; 16 | onEnterCancelled?: (el: Element) => void; 17 | 18 | onBeforeLeave?: (el: Element) => void; 19 | onLeave?: (el: Element, done: () => void) => void; 20 | onAfterLeave?: (el: Element) => void; 21 | onLeaveCancelled?: (el: Element) => void; 22 | 23 | onBeforeAppear?: (el: Element) => void; 24 | onAppear?: (el: Element, done: () => void) => void; 25 | onAfterAppear?: (el: Element) => void; 26 | onAppearCancelled?: (el: Element) => void; 27 | } 28 | 29 | export interface TransitionProps extends TransitionPropsBase { 30 | mode?: string; 31 | } 32 | 33 | export interface TransitionGroupProps extends TransitionPropsBase { 34 | tag?: string; 35 | moveClass?: string; 36 | } 37 | 38 | export interface KeepAliveProps { 39 | include?: string | RegExp | (string | RegExp)[]; 40 | exclude?: string | RegExp | (string | RegExp)[]; 41 | } 42 | -------------------------------------------------------------------------------- /types/dom.d.ts: -------------------------------------------------------------------------------- 1 | // This code is based on react definition in DefinitelyTyped published under the MIT license. 2 | // Repository: https://github.com/DefinitelyTyped/DefinitelyTyped 3 | // Path in the repository: types/react/v15/index.d.ts 4 | // 5 | // Copyrights of original definition are: 6 | // Asana 7 | // AssureSign 8 | // Microsoft 9 | // John Reilly 10 | // Benoit Benezech 11 | // Patricio Zavolinsky 12 | // Digiguru 13 | // Eric Anderson 14 | // Albert Kurniawan 15 | // Tanguy Krotoff 16 | // Dovydas Navickas 17 | // Stéphane Goetz 18 | 19 | export interface HTMLAttributes { 20 | // 'class' and 'style' aren't defined here because they exist in VNodeData 21 | accesskey?: string; 22 | contenteditable?: boolean; 23 | contextmenu?: string; 24 | dir?: string; 25 | disabled?: boolean; 26 | draggable?: boolean; 27 | hidden?: boolean; 28 | id?: string; 29 | lang?: string; 30 | spellcheck?: boolean; 31 | tabindex?: number; 32 | title?: string; 33 | 34 | role?: string; 35 | } 36 | 37 | export interface AnchorHTMLAttributes extends HTMLAttributes { 38 | download?: any; 39 | href?: string; 40 | hreflang?: string; 41 | media?: string; 42 | rel?: string; 43 | target?: string; 44 | } 45 | 46 | export interface AreaHTMLAttributes extends HTMLAttributes { 47 | alt?: string; 48 | coord?: string; 49 | download?: any; 50 | href?: string; 51 | hreflang?: string; 52 | media?: string; 53 | rel?: string; 54 | shape?: string; 55 | target?: string; 56 | } 57 | 58 | export interface AudioHTMLAttributes extends MediaHTMLAttributes {} 59 | 60 | export interface BaseHTMLAttributes extends HTMLAttributes { 61 | href?: string; 62 | target?: string; 63 | } 64 | 65 | export interface BlockquoteHTMLAttributes extends HTMLAttributes { 66 | cite?: string; 67 | } 68 | 69 | export interface ButtonHTMLAttributes extends HTMLAttributes { 70 | autofocus?: boolean; 71 | disabled?: boolean; 72 | form?: string; 73 | formaction?: string; 74 | formenctype?: string; 75 | formmethod?: string; 76 | formnovalidate?: boolean; 77 | formtarget?: string; 78 | name?: string; 79 | type?: string; 80 | value?: string | string[] | number; 81 | } 82 | 83 | export interface CanvasHTMLAttributes extends HTMLAttributes { 84 | height?: number | string; 85 | width?: number | string; 86 | } 87 | 88 | export interface ColHTMLAttributes extends HTMLAttributes { 89 | span?: number; 90 | } 91 | 92 | export interface ColgroupHTMLAttributes extends ColHTMLAttributes {} 93 | 94 | export interface DetailsHTMLAttributes extends HTMLAttributes { 95 | open?: boolean; 96 | } 97 | 98 | export interface DelHTMLAttributes extends HTMLAttributes { 99 | cite?: string; 100 | datetime?: string; 101 | } 102 | 103 | export interface EmbedHTMLAttributes extends HTMLAttributes { 104 | height?: number | string; 105 | src?: string; 106 | type?: string; 107 | width?: number | string; 108 | } 109 | 110 | export interface FieldsetHTMLAttributes extends HTMLAttributes { 111 | disabled?: boolean; 112 | form?: string; 113 | name?: string; 114 | } 115 | 116 | export interface FormHTMLAttributes extends HTMLAttributes { 117 | acceptcharset?: string; 118 | action?: string; 119 | autocomplete?: string; 120 | enctype?: string; 121 | method?: string; 122 | name?: string; 123 | novalidate?: boolean; 124 | target?: string; 125 | } 126 | 127 | export interface HtmlHTMLAttributes extends HTMLAttributes { 128 | manifest?: string; 129 | } 130 | 131 | export interface IframeHTMLAttributes extends HTMLAttributes { 132 | allowfullscreen?: boolean; 133 | allowtransparency?: boolean; 134 | frameborder?: number | string; 135 | height?: number | string; 136 | marginheight?: number; 137 | marginwidth?: number; 138 | name?: string; 139 | sandbox?: string; 140 | scrolling?: string; 141 | seamless?: boolean; 142 | src?: string; 143 | srcdoc?: string; 144 | width?: number | string; 145 | } 146 | 147 | export interface ImgHTMLAttributes extends HTMLAttributes { 148 | alt?: string; 149 | height?: number | string; 150 | sizes?: string; 151 | src?: string; 152 | srcset?: string; 153 | usemap?: string; 154 | width?: number | string; 155 | } 156 | 157 | export interface InsHTMLAttributes extends HTMLAttributes { 158 | cite?: string; 159 | datetime?: string; 160 | } 161 | 162 | export interface InputHTMLAttributes extends HTMLAttributes { 163 | accept?: string; 164 | alt?: string; 165 | autocomplete?: string; 166 | autofocus?: boolean; 167 | capture?: boolean; // https://www.w3.org/tr/html-media-capture/#the-capture-attribute 168 | checked?: boolean; 169 | crossorigin?: string; 170 | disabled?: boolean; 171 | form?: string; 172 | formaction?: string; 173 | formenctype?: string; 174 | formmethod?: string; 175 | formnovalidate?: boolean; 176 | formtarget?: string; 177 | height?: number | string; 178 | list?: string; 179 | max?: number | string; 180 | maxlength?: number; 181 | min?: number | string; 182 | minlength?: number; 183 | multiple?: boolean; 184 | name?: string; 185 | pattern?: string; 186 | placeholder?: string; 187 | readonly?: boolean; 188 | required?: boolean; 189 | size?: number; 190 | src?: string; 191 | step?: number | string; 192 | type?: string; 193 | value?: string | string[] | number; 194 | width?: number | string; 195 | } 196 | 197 | export interface KeygenHTMLAttributes extends HTMLAttributes { 198 | autofocus?: boolean; 199 | challenge?: string; 200 | disabled?: boolean; 201 | form?: string; 202 | keytype?: string; 203 | keyparams?: string; 204 | name?: string; 205 | } 206 | 207 | export interface LabelHTMLAttributes extends HTMLAttributes { 208 | for?: string; 209 | form?: string; 210 | } 211 | 212 | export interface LiHTMLAttributes extends HTMLAttributes { 213 | value?: string | string[] | number; 214 | } 215 | 216 | export interface LinkHTMLAttributes extends HTMLAttributes { 217 | href?: string; 218 | hreflang?: string; 219 | integrity?: string; 220 | media?: string; 221 | rel?: string; 222 | sizes?: string; 223 | type?: string; 224 | } 225 | 226 | export interface MapHTMLAttributes extends HTMLAttributes { 227 | name?: string; 228 | } 229 | 230 | export interface MenuHTMLAttributes extends HTMLAttributes { 231 | type?: string; 232 | } 233 | 234 | export interface MediaHTMLAttributes extends HTMLAttributes { 235 | autoplay?: boolean; 236 | controls?: boolean; 237 | crossorigin?: string; 238 | loop?: boolean; 239 | mediagroup?: string; 240 | muted?: boolean; 241 | preload?: string; 242 | src?: string; 243 | } 244 | 245 | export interface MetaHTMLAttributes extends HTMLAttributes { 246 | charset?: string; 247 | content?: string; 248 | httpequiv?: string; 249 | name?: string; 250 | } 251 | 252 | export interface MeterHTMLAttributes extends HTMLAttributes { 253 | form?: string; 254 | high?: number; 255 | low?: number; 256 | max?: number | string; 257 | min?: number | string; 258 | optimum?: number; 259 | value?: string | string[] | number; 260 | } 261 | 262 | export interface QuoteHTMLAttributes extends HTMLAttributes { 263 | cite?: string; 264 | } 265 | 266 | export interface ObjectHTMLAttributes extends HTMLAttributes { 267 | classid?: string; 268 | data?: string; 269 | form?: string; 270 | height?: number | string; 271 | name?: string; 272 | type?: string; 273 | usemap?: string; 274 | width?: number | string; 275 | wmode?: string; 276 | } 277 | 278 | export interface OlHTMLAttributes extends HTMLAttributes { 279 | reversed?: boolean; 280 | start?: number; 281 | } 282 | 283 | export interface OptgroupHTMLAttributes extends HTMLAttributes { 284 | disabled?: boolean; 285 | label?: string; 286 | } 287 | 288 | export interface OptionHTMLAttributes extends HTMLAttributes { 289 | disabled?: boolean; 290 | label?: string; 291 | selected?: boolean; 292 | value?: string | string[] | number; 293 | } 294 | 295 | export interface OutputHTMLAttributes extends HTMLAttributes { 296 | for?: string; 297 | form?: string; 298 | name?: string; 299 | } 300 | 301 | export interface ParamHTMLAttributes extends HTMLAttributes { 302 | name?: string; 303 | value?: string | string[] | number; 304 | } 305 | 306 | export interface ProgressHTMLAttributes extends HTMLAttributes { 307 | max?: number | string; 308 | value?: string | string[] | number; 309 | } 310 | 311 | export interface ScriptHTMLAttributes extends HTMLAttributes { 312 | async?: boolean; 313 | charset?: string; 314 | crossorigin?: string; 315 | defer?: boolean; 316 | integrity?: string; 317 | nonce?: string; 318 | src?: string; 319 | type?: string; 320 | } 321 | 322 | export interface SelectHTMLAttributes extends HTMLAttributes { 323 | autofocus?: boolean; 324 | disabled?: boolean; 325 | form?: string; 326 | multiple?: boolean; 327 | name?: string; 328 | required?: boolean; 329 | size?: number; 330 | value?: string | string[] | number; 331 | } 332 | 333 | export interface SourceHTMLAttributes extends HTMLAttributes { 334 | media?: string; 335 | sizes?: string; 336 | src?: string; 337 | srcset?: string; 338 | type?: string; 339 | } 340 | 341 | export interface StyleHTMLAttributes extends HTMLAttributes { 342 | media?: string; 343 | nonce?: string; 344 | scoped?: boolean; 345 | type?: string; 346 | } 347 | 348 | export interface TableHTMLAttributes extends HTMLAttributes { 349 | cellpadding?: number | string; 350 | cellspacing?: number | string; 351 | summary?: string; 352 | } 353 | 354 | export interface TextareaHTMLAttributes extends HTMLAttributes { 355 | autocomplete?: string; 356 | autofocus?: boolean; 357 | cols?: number; 358 | dirname?: string; 359 | disabled?: boolean; 360 | form?: string; 361 | maxlength?: number; 362 | minlength?: number; 363 | name?: string; 364 | placeholder?: string; 365 | readonly?: boolean; 366 | required?: boolean; 367 | rows?: number; 368 | value?: string | string[] | number; 369 | wrap?: string; 370 | } 371 | 372 | export interface TdHTMLAttributes extends HTMLAttributes { 373 | colspan?: number; 374 | headers?: string; 375 | rowspan?: number; 376 | } 377 | 378 | export interface ThHTMLAttributes extends HTMLAttributes { 379 | colspan?: number; 380 | headers?: string; 381 | rowspan?: number; 382 | scope?: string; 383 | } 384 | 385 | export interface TimeHTMLAttributes extends HTMLAttributes { 386 | datetime?: string; 387 | } 388 | 389 | export interface TrackHTMLAttributes extends HTMLAttributes { 390 | default?: boolean; 391 | kind?: string; 392 | label?: string; 393 | src?: string; 394 | srclang?: string; 395 | } 396 | 397 | export interface VideoHTMLAttributes extends MediaHTMLAttributes { 398 | height?: number | string; 399 | playsinline?: boolean; 400 | poster?: string; 401 | width?: number | string; 402 | } 403 | 404 | export interface AllHTMLAttributes extends HTMLAttributes { 405 | accept?: string; 406 | acceptcharset?: string; 407 | action?: boolean; 408 | allowfullscreen?: boolean; 409 | allowtransparency?: boolean; 410 | alt?: string; 411 | async?: boolean; 412 | autocomplete?: string; 413 | autofocus?: boolean; 414 | autoplay?: boolean; 415 | capture?: boolean; // https://www.w3.org/tr/html-media-capture/#the-capture-attribute 416 | cellpadding?: number | string; 417 | cellspacing?: number | string; 418 | challenge?: string; 419 | charset?: string; 420 | checked?: boolean; 421 | cite?: string; 422 | classid?: string; 423 | cols?: number; 424 | colspan?: number; 425 | content?: string; 426 | controls?: boolean; 427 | coord?: string; 428 | crossorigin?: string; 429 | data?: string; 430 | datetime?: string; 431 | default?: boolean; 432 | defer?: boolean; 433 | dirname?: string; 434 | disabled?: boolean; 435 | download?: any; 436 | enctype?: string; 437 | for?: string; 438 | form?: string; 439 | formaction?: string; 440 | formenctype?: string; 441 | formmethod?: string; 442 | formnovalidate?: boolean; 443 | formtarget?: string; 444 | frameborder?: number | string; 445 | headers?: string; 446 | height?: number | string; 447 | high?: number; 448 | href?: string; 449 | hreflang?: string; 450 | httpequiv?: string; 451 | integrity?: string; 452 | keyparams?: string; 453 | keytype?: string; 454 | kind?: string; 455 | label?: string; 456 | list?: string; 457 | loop?: boolean; 458 | low?: number; 459 | manifest?: string; 460 | marginheight?: number; 461 | marginwidth?: number; 462 | max?: number | string; 463 | maxlength?: number; 464 | media?: string; 465 | mediagroup?: string; 466 | method?: string; 467 | min?: number | string; 468 | minlength?: number; 469 | multiple?: boolean; 470 | muted?: boolean; 471 | name?: string; 472 | nonce?: string; 473 | novalidate?: boolean; 474 | open?: boolean; 475 | optimum?: number; 476 | pattern?: string; 477 | placeholder?: string; 478 | playsinline?: boolean; 479 | poster?: string; 480 | preload?: string; 481 | readonly?: boolean; 482 | rel?: string; 483 | required?: boolean; 484 | reversed?: boolean; 485 | rows?: number; 486 | rowspan?: number; 487 | sandbox?: string; 488 | scope?: string; 489 | scoped?: boolean; 490 | scrolling?: string; 491 | seamless?: boolean; 492 | selected?: boolean; 493 | shape?: string; 494 | size?: number; 495 | sizes?: string; 496 | span?: number; 497 | src?: string; 498 | srcdoc?: string; 499 | srclang?: string; 500 | srcset?: string; 501 | start?: number; 502 | step?: number | string; 503 | summary?: string; 504 | target?: string; 505 | type?: string; 506 | usemap?: string; 507 | value?: string | string[] | number; 508 | width?: number | string; 509 | wmode?: string; 510 | wrap?: string; 511 | } 512 | 513 | export type ElementType = [E, A]; 514 | 515 | export interface IntrinsicElementTypes { 516 | a: ElementType; 517 | abbr: ElementType; 518 | address: ElementType; 519 | area: ElementType; 520 | article: ElementType; 521 | aside: ElementType; 522 | audio: ElementType; 523 | b: ElementType; 524 | base: ElementType; 525 | bdi: ElementType; 526 | bdo: ElementType; 527 | big: ElementType; 528 | blockquote: ElementType; 529 | body: ElementType; 530 | br: ElementType; 531 | button: ElementType; 532 | canvas: ElementType; 533 | caption: ElementType; 534 | cite: ElementType; 535 | code: ElementType; 536 | col: ElementType; 537 | colgroup: ElementType; 538 | data: ElementType; 539 | datalist: ElementType; 540 | dd: ElementType; 541 | del: ElementType; 542 | details: ElementType; 543 | dfn: ElementType; 544 | dialog: ElementType; 545 | div: ElementType; 546 | dl: ElementType; 547 | dt: ElementType; 548 | em: ElementType; 549 | embed: ElementType; 550 | fieldset: ElementType; 551 | figcaption: ElementType; 552 | figure: ElementType; 553 | footer: ElementType; 554 | form: ElementType; 555 | h1: ElementType; 556 | h2: ElementType; 557 | h3: ElementType; 558 | h4: ElementType; 559 | h5: ElementType; 560 | h6: ElementType; 561 | head: ElementType; 562 | header: ElementType; 563 | hgroup: ElementType; 564 | hr: ElementType; 565 | html: ElementType; 566 | i: ElementType; 567 | iframe: ElementType; 568 | img: ElementType; 569 | input: ElementType; 570 | ins: ElementType; 571 | kbd: ElementType; 572 | keygen: [HTMLElement, KeygenHTMLAttributes]; 573 | label: ElementType; 574 | legend: ElementType; 575 | li: ElementType; 576 | link: ElementType; 577 | main: ElementType; 578 | map: ElementType; 579 | mark: ElementType; 580 | menu: ElementType; 581 | menuitem: ElementType; 582 | meta: ElementType; 583 | meter: ElementType; 584 | nav: ElementType; 585 | noscript: ElementType; 586 | object: ElementType; 587 | ol: ElementType; 588 | optgroup: ElementType; 589 | option: ElementType; 590 | output: ElementType; 591 | p: ElementType; 592 | param: ElementType; 593 | picture: ElementType; 594 | pre: ElementType; 595 | progress: ElementType; 596 | q: ElementType; 597 | rp: ElementType; 598 | rt: ElementType; 599 | ruby: ElementType; 600 | s: ElementType; 601 | samp: ElementType; 602 | script: ElementType; 603 | section: ElementType; 604 | select: ElementType; 605 | small: ElementType; 606 | source: ElementType; 607 | span: ElementType; 608 | strong: ElementType; 609 | style: ElementType; 610 | sub: ElementType; 611 | summary: ElementType; 612 | sup: ElementType; 613 | table: ElementType; 614 | tbody: ElementType; 615 | td: ElementType; 616 | textarea: ElementType; 617 | tfoot: ElementType; 618 | th: ElementType; 619 | thead: ElementType; 620 | time: ElementType; 621 | title: ElementType; 622 | tr: ElementType; 623 | track: ElementType; 624 | u: ElementType; 625 | ul: ElementType; 626 | var: ElementType; 627 | video: ElementType; 628 | wbr: ElementType; 629 | } 630 | 631 | export type IntrinsicElementAttributes = { 632 | [K in keyof IntrinsicElementTypes]: IntrinsicElementTypes[K][1]; 633 | }; 634 | 635 | export interface Events { 636 | // clipboard events 637 | copy: ClipboardEvent; 638 | cut: ClipboardEvent; 639 | paste: ClipboardEvent; 640 | 641 | // composition events 642 | compositionend: CompositionEvent; 643 | compositionstart: CompositionEvent; 644 | compositionupdate: CompositionEvent; 645 | 646 | // drag drop events 647 | drag: DragEvent; 648 | dragend: DragEvent; 649 | dragenter: DragEvent; 650 | dragexit: DragEvent; 651 | dragleave: DragEvent; 652 | dragover: DragEvent; 653 | dragstart: DragEvent; 654 | drop: DragEvent; 655 | 656 | // focus events 657 | focus: SyntheticEvent; 658 | blur: SyntheticEvent; 659 | 660 | // form events 661 | change: SyntheticEvent; 662 | input: SyntheticEvent; 663 | reset: SyntheticEvent; 664 | submit: SyntheticEvent; 665 | invalid: SyntheticEvent; 666 | 667 | // image events 668 | load: Event; 669 | error: Event; 670 | 671 | // keyboard events 672 | keydown: KeyboardEvent; 673 | keypress: KeyboardEvent; 674 | keyup: KeyboardEvent; 675 | 676 | // mouse events 677 | click: MouseEvent; 678 | contextmenu: MouseEvent; 679 | dblclick: MouseEvent; 680 | mousedown: MouseEvent; 681 | mouseenter: MouseEvent; 682 | mouseleave: MouseEvent; 683 | mousemove: MouseEvent; 684 | mouseout: MouseEvent; 685 | mouseover: MouseEvent; 686 | mouseup: MouseEvent; 687 | 688 | // media events 689 | abort: Event; 690 | canplay: Event; 691 | canplaythrough: Event; 692 | durationchange: Event; 693 | emptied: Event; 694 | encrypted: Event; 695 | ended: Event; 696 | loadeddata: Event; 697 | loadedmetadata: Event; 698 | loadstart: Event; 699 | pause: Event; 700 | play: Event; 701 | playing: Event; 702 | progress: Event; 703 | ratechange: Event; 704 | seeked: Event; 705 | seeking: Event; 706 | stalled: Event; 707 | suspend: Event; 708 | timeupdate: Event; 709 | volumechange: Event; 710 | waiting: Event; 711 | 712 | // selection events 713 | select: Event; 714 | 715 | // UI events 716 | scroll: UIEvent; 717 | 718 | // touch events 719 | touchcancel: TouchEvent; 720 | touchend: TouchEvent; 721 | touchmove: TouchEvent; 722 | touchstart: TouchEvent; 723 | 724 | // wheel events 725 | wheel: WheelEvent; 726 | 727 | // animation events 728 | animationstart: AnimationEvent; 729 | animationend: AnimationEvent; 730 | animationiteration: AnimationEvent; 731 | 732 | // transition events 733 | transitionend: TransitionEvent; 734 | transitionstart: TransitionEvent; 735 | } 736 | 737 | type SyntheticEvent = { 738 | target: EventTarget & T; 739 | } & Pick>; 740 | 741 | export interface EventsOn { 742 | // clipboard events 743 | onCopy: ClipboardEvent; 744 | onCut: ClipboardEvent; 745 | onPaste: ClipboardEvent; 746 | 747 | // composition events 748 | onCompositionend: CompositionEvent; 749 | onCompositionstart: CompositionEvent; 750 | onCompositionupdate: CompositionEvent; 751 | 752 | // drag drop events 753 | onDrag: DragEvent; 754 | onDragend: DragEvent; 755 | onDragenter: DragEvent; 756 | onDragexit: DragEvent; 757 | onDragleave: DragEvent; 758 | onDragover: DragEvent; 759 | onDragstart: DragEvent; 760 | onDrop: DragEvent; 761 | 762 | // focus events 763 | onFocus: SyntheticEvent; 764 | onBlur: SyntheticEvent; 765 | 766 | // form events 767 | onChange: SyntheticEvent; 768 | onInput: SyntheticEvent; 769 | onReset: SyntheticEvent; 770 | onSubmit: SyntheticEvent; 771 | onInvalid: SyntheticEvent; 772 | 773 | // image events 774 | onLoad: Event; 775 | onError: Event; 776 | 777 | // keyboard events 778 | onKeydown: KeyboardEvent; 779 | onKeypress: KeyboardEvent; 780 | onKeyup: KeyboardEvent; 781 | 782 | // mouse events 783 | onClick: MouseEvent; 784 | onContextmenu: MouseEvent; 785 | onDblclick: MouseEvent; 786 | onMousedown: MouseEvent; 787 | onMouseenter: MouseEvent; 788 | onMouseleave: MouseEvent; 789 | onMousemove: MouseEvent; 790 | onMouseout: MouseEvent; 791 | onMouseover: MouseEvent; 792 | onMouseup: MouseEvent; 793 | 794 | // media events 795 | onAbort: Event; 796 | onCanplay: Event; 797 | onCanplaythrough: Event; 798 | onDurationchange: Event; 799 | onEmptied: Event; 800 | onEncrypted: Event; 801 | onEnded: Event; 802 | onLoadeddata: Event; 803 | onLoadedmetadata: Event; 804 | onLoadstart: Event; 805 | onPause: Event; 806 | onPlay: Event; 807 | onPlaying: Event; 808 | onProgress: Event; 809 | onRatechange: Event; 810 | onSeeked: Event; 811 | onSeeking: Event; 812 | onStalled: Event; 813 | onSuspend: Event; 814 | onTimeupdate: Event; 815 | onVolumechange: Event; 816 | onWaiting: Event; 817 | 818 | // selection events 819 | onSelect: Event; 820 | 821 | // UI events 822 | onScroll: UIEvent; 823 | 824 | // touch events 825 | onTouchcancel: TouchEvent; 826 | onTouchend: TouchEvent; 827 | onTouchmove: TouchEvent; 828 | onTouchstart: TouchEvent; 829 | 830 | // wheel events 831 | onWheel: WheelEvent; 832 | 833 | // animation events 834 | onAnimationstart: AnimationEvent; 835 | onAnimationend: AnimationEvent; 836 | onAnimationiteration: AnimationEvent; 837 | 838 | // transition events 839 | onTransitionend: TransitionEvent; 840 | onTransitionstart: TransitionEvent; 841 | } 842 | 843 | export interface EventsNativeOn { 844 | // clipboard events 845 | nativeOnCopy: ClipboardEvent; 846 | nativeOnCut: ClipboardEvent; 847 | nativeOnPaste: ClipboardEvent; 848 | 849 | // composition events 850 | nativeOnCompositionend: CompositionEvent; 851 | nativeOnCompositionstart: CompositionEvent; 852 | nativeOnCompositionupdate: CompositionEvent; 853 | 854 | // drag drop events 855 | nativeOnDrag: DragEvent; 856 | nativeOnDragend: DragEvent; 857 | nativeOnDragenter: DragEvent; 858 | nativeOnDragexit: DragEvent; 859 | nativeOnDragleave: DragEvent; 860 | nativeOnDragover: DragEvent; 861 | nativeOnDragstart: DragEvent; 862 | nativeOnDrop: DragEvent; 863 | 864 | // focus events 865 | nativeOnFocus: FocusEvent; 866 | nativeOnBlur: FocusEvent; 867 | 868 | // form events 869 | nativeOnChange: Event; 870 | nativeOnInput: Event; 871 | nativeOnReset: Event; 872 | nativeOnSubmit: Event; 873 | nativeOnInvalid: Event; 874 | 875 | // image events 876 | nativeOnLoad: Event; 877 | nativeOnError: Event; 878 | 879 | // keyboard events 880 | nativeOnKeydown: KeyboardEvent; 881 | nativeOnKeypress: KeyboardEvent; 882 | nativeOnKeyup: KeyboardEvent; 883 | 884 | // mouse events 885 | nativeOnClick: MouseEvent; 886 | nativeOnContextmenu: MouseEvent; 887 | nativeOnDblclick: MouseEvent; 888 | nativeOnMousedown: MouseEvent; 889 | nativeOnMouseenter: MouseEvent; 890 | nativeOnMouseleave: MouseEvent; 891 | nativeOnMousemove: MouseEvent; 892 | nativeOnMouseout: MouseEvent; 893 | nativeOnMouseover: MouseEvent; 894 | nativeOnMouseup: MouseEvent; 895 | 896 | // media events 897 | nativeOnAbort: Event; 898 | nativeOnCanplay: Event; 899 | nativeOnCanplaythrough: Event; 900 | nativeOnDurationchange: Event; 901 | nativeOnEmptied: Event; 902 | nativeOnEncrypted: Event; 903 | nativeOnEnded: Event; 904 | nativeOnLoadeddata: Event; 905 | nativeOnLoadedmetadata: Event; 906 | nativeOnLoadstart: Event; 907 | nativeOnPause: Event; 908 | nativeOnPlay: Event; 909 | nativeOnPlaying: Event; 910 | nativeOnProgress: Event; 911 | nativeOnRatechange: Event; 912 | nativeOnSeeked: Event; 913 | nativeOnSeeking: Event; 914 | nativeOnStalled: Event; 915 | nativeOnSuspend: Event; 916 | nativeOnTimeupdate: Event; 917 | nativeOnVolumechange: Event; 918 | nativeOnWaiting: Event; 919 | 920 | // selection events 921 | nativeOnSelect: Event; 922 | 923 | // UI events 924 | nativeOnScroll: UIEvent; 925 | 926 | // touch events 927 | nativeOnTouchcancel: TouchEvent; 928 | nativeOnTouchend: TouchEvent; 929 | nativeOnTouchmove: TouchEvent; 930 | nativeOnTouchstart: TouchEvent; 931 | 932 | // wheel events 933 | nativeOnWheel: WheelEvent; 934 | 935 | // animation events 936 | nativeOnAnimationstart: AnimationEvent; 937 | nativeOnAnimationend: AnimationEvent; 938 | nativeOnAnimationiteration: AnimationEvent; 939 | 940 | // transition events 941 | nativeOnTransitionend: TransitionEvent; 942 | nativeOnTransitionstart: TransitionEvent; 943 | } 944 | -------------------------------------------------------------------------------- /types/v2-compat.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VueConstructor } from "vue"; 2 | import { KnownAttrs, ScopedSlotHandlers, EventHandlers } from "./base"; 3 | 4 | export type TsxComponentAttrs = 5 | | ({ props: TProps } & Partial & 6 | KnownAttrs & { 7 | scopedSlots?: ScopedSlotHandlers; 8 | } & EventHandlers) 9 | | (TProps & 10 | KnownAttrs & { 11 | scopedSlots?: ScopedSlotHandlers; 12 | } & EventHandlers); 13 | 14 | export type TsxComponentInstance = { 15 | _tsxattrs: TsxComponentAttrs; 16 | } & V; 17 | 18 | export type TsxComponent< 19 | V extends Vue, 20 | Props = {}, 21 | EventsWithOn = {}, 22 | ScopedSlotArgs = {}, 23 | AdditionalThisAttrs = {} 24 | > = VueConstructor< 25 | TsxComponentInstance & AdditionalThisAttrs 26 | >; 27 | --------------------------------------------------------------------------------