总结
- 这个库帮助我们在 tsx 中实现 props 等的提醒
- 在 TS 中组件能够传递 props 建议都要定义好类型
class component
import * as tsx from "vue-tsx-support";
interface MyComponentProps {
text: string;
important?: boolean;
}
interface Events {
onOk: void;
onError: { code: number, detail: string }; // 参数只有一个,类型为接口 { code: number, detail: string }
}
interface ScopedSlots {
default: { text: string }; // 参数只有一个,类型为接口 { text: string }
}
@component // 不是该库提供
class MyComponent extends tsx.Component<MyComponentProps, Events, ScopedSlots> {
@Props({required:true}) text!:string; // 不是该库提供
@Props() important!:boolean; // 不是该库提供
/* snip */
}
modifiers 事件修饰符
- esc, tab, enter, space, up, down, del, left, right 键盘事件
- del 会被 DELETE 或 BACKSPACE 触发
- m.enter.esc 的方式组合修饰符,并不能够成功
- left, right, middle 鼠标事件
- ctrl, shift, alt, meta 系统修饰符键
- noctrl, noshift, noalt, nometa 仅当未按下该修饰符时才会被触发
- self
- prevent(不触发原生事件动作), stop
- keys(...args) 多个按键之一触发
- exact(...args) 系统修饰符精确匹配时触发
import { modifiers as m } from "vue-tsx-support";
<div onKeydown={m.enter(this.onEnter)} />;
<div onKeydown={m.enter.prevent(this.onEnter)} />;
<div onKeydown={m.esc.prevent} />;
<div onKeydown={m.keys("enter", "esc")(this.onEnterOrEsc)} />; // <div @keydown.enter.esc="onEnterOrEsc" /> 按下 enter 或 esc 时触发
<div onKeydown={m.keys(65).exact("ctrl", "alt")(this.onCtrlAltA)} />; // 65 代表 a 键
<div onClick={m.exact("ctrl", "shift")(handler)} />;
安装
- 在 tsconfig.json 配置,启用 tsx 的类型提醒
npm install vue-tsx-support -S
{
"compilerOptions": {
"...snip...": "...snip..."
},
"include": [
"node_modules/vue-tsx-support/enable-check.d.ts",
"...snip..."
]
}
- 要启用某个选项将它们导入即可,例如:allow-unknown-props
import "vue-tsx-support/options/allow-unknown-props";
- allow-element-unknown-attrs 在原生元素上允许非 attrs 属性
- allow-unknown-props 在组件上允许非 props 属性
- enable-html-attrs 在组件上允许非 props 但是是 attrs 的属性,并且遵循 attrs 的类型校验
- enable-nativeon 在组件上允许使用 nativeOn 的方式绑定原生事件
- enable-vue-router 添加 router-link 和 router-view 定义
不常用——————————————————————————————————
ofType 给现有组件添加类型提醒
import ThirdPartyComponent from "third-party-library";
import * as tsx from "vue-tsx-support";
interface MyComponentProps { /* ... */ }
const MyComponent = tsx.ofType<MyComponentProps>().convert(ThirdPartyComponent);
// const MyComponent = tsx.ofType<MyComponentProps, Events, ScopedSlots>().convert(ThirdPartyComponent);
Vue.component 或 Vue.extend 定义组件
props
- 不支持 props 通过数组定义,例如:
props: ["foo", "bar"]
- 所有 props 都会被 ts 视为可选,即使设置了 required:true。有 3 种办法解决
- 使用 as 的方式
text: { type: String, required: true as true }
- 使用 vue-strict-prop 库,
import p from "vue-strict-prop";
后text: p(String).required
- 让必传 props 作为 tsx.componentFactory.create 的第二参数
tsx.componentFactory.create({...}, ["text"])
componentFactoryOf 使 event 和 scoped slots 具有 ts 提醒
interface Events {
onOk: () => void; // 以 on 开头
onError: { code: number, detail: string }; // 参数只有一个时的简易写法,onError 的参数的类型为接口 { code: number, detail: string }
}
interface ScopedSlots {
default: { text: string }; // default 作用域插槽将接收到一个参数类型为接口 { text: string }
optional?: string; // 不仅插槽的参数会有类型提醒,插槽自身也会有必填提醒
}
const MyComponent = tsx.componentFactoryOf<Events, ScopedSlots>().create({
...
})
componentFactory 构建 vue 的子类
tsx.component
是 tsx.componentFactory.create
的简写
import * as tsx from "vue-tsx-support";
const MyComponent = tsx.componentFactory.create({
props: {
text: { type: String, required: true },
important: Boolean,
},
computed: {
className(): string {
return this.important ? "label-important" : "label-normal";
}
},
methods: {
onClick(event) { this.$emit("ok", event); }
},
render(): VNode {
return <span class={this.className} onClick={this.onClick}>{this.text}</span>;
}
});
extendFrom 继承
import * as tsx from "vue-tsx-support";
// 注释:const Base = Vue.extend({/* snip */})
// This is equivalent to `const MyComponent = Base.extend({ /* snip */ });`
const MyComponent = tsx.extendFrom(Base).create({
/* snip */
});
mixin 混入
import * as tsx from "vue-tsx-support";
const StorageMixin = {
... // vue options
}
const MyComponent = tsx.componentFactory.mixin(StorageMixin).create({
... // vue options
})
const tsx.componentFactory.mixin(FirstMixin).mixin(SecondMixin).create({
/* snip */
})
————————————————————————————————————————————————————————————————
Install and enable
npm install vue-tsx-support -S
- And refer vue-tsx-support/enable-check.d.ts from somewhere to enable compiler check. 类型说明文档在 vue-tsx-support/enable-check.d.ts 可以通过 import 引入或 tsconfig.json 配置
- 注释:启用后在 @vue/cli 生成的项目中,会和 shims-tsx.d.ts 的 IntrinsicElements 出现重复定义
import "vue-tsx-support/enable-check"
{
"compilerOptions": {
"...snip...": "...snip..."
},
"include": [
"node_modules/vue-tsx-support/enable-check.d.ts",
"...snip..."
]
}
Using intrinsic elements 使用原生元素
- 注释:不需要这个库也能够在 tsx 中为原生元素提供提醒
Using custom component 使用自定义组件
- By default, vue-tsx-support does not allow unknown props. vue-tsx-support 默认时不允许在 tsx 中向元素传入未定义的 props
- You must add types to the component by apis memtions below, or enable allow-unknown-props option.可以通过设置 allow-unknown-props 不出现这个报错(不建议)
componentFactory
- componentFactory.create can infer types of props from component options same as Vue.extend. 能够自动推断类型,例如下例中的 props
- shorthand props definition(like props: ["foo", "bar"]) is currently not supported. 不支持 props 通过数组定义,例如:
props: ["foo", "bar"]
- all props are regarded as optional even if required: true specified. 但是所有 props 都会被 ts 视为可选,即使设置了 required:true。有 7 种办法解决?
import * as tsx from "vue-tsx-support";
const MyComponent = tsx.componentFactory.create({
props: {
text: { type: String, required: true },
important: Boolean,
},
computed: {
className(): string {
return this.important ? "label-important" : "label-normal";
}
},
methods: {
onClick(event) { this.$emit("ok", event); }
},
render(): VNode {
return <span class={this.className} onClick={this.onClick}>{this.text}</span>;
}
});
- 1.Instead of required: true, specify required: true as true. 使用
required: true as true
代替required: true
import * as tsx from "vue-tsx-support";
const MyComponent = tsx.componentFactory.create({
props: {
text: { type: String, required: true as true },
important: Boolean,
},
/* snip */
});
import * as tsx from "vue-tsx-support";
import p from "vue-strict-prop";
const MyComponent = tsx.componentFactory.create({
props: {
text: p(String).required,
important: Boolean,
},
/* snip */
});
- 3.Specify required prop names as second argument 让必传 props 作为 tsx.componentFactory.create 的第二参数
import * as tsx from "vue-tsx-support";
const MyComponent = tsx.componentFactory.create({
props: {
text: { type: String, required: true },
important: Boolean,
},
/* snip */
}, ["text"]);
component
- Shorthand of componentFactory.create
tsx.component
是tsx.componentFactory.create
的简写
extendFrom
- When you want to extend your component from other than Vue, you can use extendFrom 从 Vue 子类创建构造函数
import * as tsx from "vue-tsx-support";
// 注释:const Base = Vue.extend({/* snip */})
// This is equivalent to `const MyComponent = Base.extend({ /* snip */ });`
const MyComponent = tsx.extendFrom(Base).create({
/* snip */
});
mixin
import * as tsx from "vue-tsx-support";
const StorageMixin = {
methods: {
getItem(string name): string {
return localStorage.getItem(name);
},
setItem(string name, string value): void {
localStorage.setItem(name, value);
}
}
}
const MyComponent = tsx.componentFactory.mixin(StorageMixin).create(
{
props: {
name: String
},
data() {
return { value: "" }
},
mounted() {
this.value = this.getItem(this.name);
},
render(): VNode {
return (
<button onClick={() => this.setItem(this.name, this.value)}>
SAVE
</button>
);
}
}
);
// You can add 2 or more mixins by method chain
const tsx.componentFactory.mixin(FirstMixin).mixin(SecondMixin).create({
/* snip */
})
componentFactoryOf
- Return componentFactory with additional types (events and scoped slots) 使 event 和 scoped slots 具有 ts 提醒
import * as tsx from "vue-tsx-support";
interface Events {
// all memebers must be prefixed by 'on' 必须以 on 开头,这是 jsx 的要求
onOk: () => void;
// If event handler has only one parameter, you can specify parameter type as a shorthand.
// For example, this is equivalent to `onError: (arg: { code: number, detail: string }) => void`
// 注释:如果该 event 只有一个参数,可以使用简易写法,这里的 onError 的参数类型为 { code: number, detail: string } 这样的一个接口
onError: { code: number, detail: string };
}
const MyComponent = tsx.componentFactoryOf<Events>().create({
render(): VNode {
return (
<div>
<button onClick={() => this.$emit("ok")}>OK</button>
<button onClick={() => this.$emit("error", { code: 9, detail: "unknown" })}>Raise Error</button>
</div>
);
}
});
<MyComponent onOk={() => console.log("ok")} />;
<MyComponent onError={p => console.log("ng", p.code, p.detail)} />;
import * as tsx from "vue-tsx-support";
interface ScopedSlots {
default: { text: string };
optional?: string;
}
const MyComponent = tsx.componentFactoryOf<{}, ScopedSlots>().create({
props: {
text: String
},
render(): VNode {
const { default, optional } = this.$scopedSlots;
return <ul>
<li>{ default({ text: this.text || "default text" }) }</li>
<li>{ optional ? optional(this.text) : this.text }<li>
</ul>;
}
});
<MyComponent scopedSlots={{
default: p => <span>p.text</span>
}}
/>;
// NG: 'default' is missing in scopedSlots 不仅插槽的参数会有类型提醒,插槽自身也会有必填提醒
<MyComponent scopedSlots={{
optional: p => <span>p</span>
}}
/>;
Component
import component from "vue-class-component";
import * as tsx from "vue-tsx-support";
interface MyComponentProps {
text: string;
important?: boolean;
}
@component({
// 这是旧的写法了,推荐使用 @Prop
props: {
text: { type: String, required: true },
important: Boolean
},
/* snip */
})
class MyComponent extends tsx.Component<MyComponentProps> {
/* snip */
}
- If you want, you can specify event types and scoped slot types as 2nd and 3rd type parameter 参数分别是 props、events、scoped slot 的类型
import component from "vue-class-component";
import * as tsx from "vue-tsx-support";
interface MyComponentProps {
text: string;
important?: boolean;
}
interface Events {
onOk: void;
onError: { code: number, detail: string };
}
interface ScopedSlots {
default: { text: string };
}
@component({
props: {
text: { type: String, required: true },
important: Boolean
},
/* snip */
})
class MyComponent extends tsx.Component<MyComponentProps, Events, ScopedSlots> {
/* snip */
}
ofType
- If you can't modify existing component definition, wrap it by ofType and convert 给现有组件添加类型提醒
import ThirdPartyComponent from "third-party-library";
import * as tsx from "vue-tsx-support";
interface MyComponentProps { /* ... */ }
const MyComponent = tsx.ofType<MyComponentProps>().convert(ThirdPartyComponent);
// const MyComponent = tsx.ofType<MyComponentProps, Events, ScopedSlots>().convert(ThirdPartyComponent);
Other attributes 其他属性
Native event listeners and dom properties 原生 event 和属性
- To avoid compilation error, you must use kebab-case attribute name. 在组件上绑定原生属性和方法不支持 JSX 的写法,会出现编译错误。例如
nativeOnClick={} domPropInnerHTML={}
会报错
- 注释:以下两种方法都没有类型提醒
<Component nativeOn-click={ ... } />
<Component domProp-innerHTML={ ... } />
- Or use JSX-spread style. 或者 JSX-spread 的方式
<Component { ...{ nativeOn: { click: ... } } } />
<Component { ...{ domProps: { innerHTML: ... } } } />
HTML attributes attached to the root element 附加到组件上的 HTML 属性
<SomeInputComponent { ...{ attrs: { min: 0, max: 100 } } } />
Options
- To enable each options, import them somewhere 要启用某个选项将它们导入即可,例如:allow-unknown-props
import "vue-tsx-support/options/allow-unknown-props";
allow-element-unknown-attrs
- Make enabled to specify unknown attributes to intrinsic elements 在原生元素上允许非 attrs 属性
<div foo="foo" />;
allow-unknown-props
- Make enabled to specify unknown props to Vue component. 在组件上允许非 props 属性
enable-html-attrs
- Make enabled to specify HTML attributes to Vue component. 在组件上允许非 props 但是是 attrs 的属性,并且遵循 attrs 的类型校验
<MyComponent foo="foo" min={ 0 } max={ 100 } />;
enable-nativeon
- Make enabled to specify native event listeners to Vue component. 在组件上允许使用 nativeOn 的方式绑定原生事件
<MyComponent foo="foo" nativeOnClick={ e => ... } />; // and `e` is infered as MouseEvent e 将被推断为 MouseEvent 事件对象
enable-vue-router
- Add definitions of router-link and router-view 添加 router-link 和 router-view 定义
Utility
modifiers
- Event handler wrappers which work like some event modifiers available in template 事件处理包装函数,等同于在模板中为事件添加修饰符。例如 .enter 等
import { modifiers as m } from "vue-tsx-support";
<div onKeydown={m.enter(this.onEnter)} />;
<div onKeydown={m.enter.prevent(this.onEnter)} />;
<div onKeydown={m.esc.prevent} />;
<div onKeydown={m.keys("enter", "esc")(this.onEnterOrEsc)} />; // <div @keydown.enter.esc="onEnterOrEsc" /> 按下 enter 或 esc 时触发
<div onKeydown={m.keys(65).exact("ctrl", "alt")(this.onCtrlAltA)} />;
Available modifiers 可以用的修饰器
- esc, tab, enter, space, up, down, del, left, right 键盘事件
- del allows not only DELETE, but also BACKSPACE. del 会被 DELETE 或 BACKSPACE 触发
- left and right have another behavior when specified to mouse event 在鼠标事件中使用 left 和 right 会在点击鼠标左键或右键时触发
- combination of key modifiers (e.g. m.enter.esc) does not work. m.enter.esc 的方式组合修饰符,并不能够成功
- left, right, middle 鼠标事件
- ctrl, shift, alt, meta 系统修饰符键
- noctrl, noshift, noalt, nometa
- Execute event handler only when specified system modifier key is not pressed. 仅当未按下该修饰符时才会被触发
- self
- prevent(不触发原生事件动作), stop
- keys(...args)
- Known key name("esc", "tab", "enter", ...) or number can be specified. 支持数字,例如
<div onKeydown={m.keys(65)(handler)} />; // 65 代表 a 键
- exact(...args) 系统修饰符精确匹配时触发,例如:
<div onClick={m.exact("ctrl", "shift")(handler)} />;