vue中的使用场景
tsconfig注意事项
注意你需要引入 strict: true (或者至少 noImplicitThis: true,这是 strict 模式的一部分) 以利用组件方法中 this 的类型检查,否则它会始终被看作 any 类型。
需要注意以下的配置项,不然类型推断不能正常运作
// 启用所有严格类型检查选项
"strict": true,
// 禁止this 值为 any
"noImplicitThis": true,
Vue.extend()的作用
vue.extend方法已经声明了参数类型,所以vue.extend可以为你提供类型推断。
举个例子,看下面一段代码:
type vueExtend = (option: { name: string, $store: object, $route: object }) => void;
const vueInstance: vueExtend = ({ $ }) {};
vue原型混入属性或方法
平时我们写的 $store 或 $route 等方法插件向vue原型中添加了原型属性$store和$route。
那如何在ts中的vue原型中混入这些属性或方法呢?
从vuex导出的ts声明文件,我们可以看到下代码:
import Vue, { ComponentOptions } from "vue";
import { Store } from "./index";
declare module "vue/types/options" {
interface ComponentOptions<V extends Vue> {
store?: Store<any>;
}
}
declare module "vue/types/vue" {
interface Vue {
$store: Store<any>;
}
}
所以如果我们想混入$api的话也是需要这么做的。
先去新建一个global.d.ts,使用以下的代码
declare module "vue/types/vue" {
interface Vue {
$api: {
queryHotel: <T>(options: any) => Promise<T>;
};
}
// 使用
this.$api.queryHotel(payload).then(() => {});
推断vue组件实例类型
为什么需要推断vue实例类型,这是有使用场景的。
比如我们需要拿到子组件的引用,并调用子组件的方法的时候。
假设此时子组件为SpaceDrawer, ref为spaceDrawer。
// 同步移除物联树的节点
(this.$refs.spaceDrawer as any).removeCheckNode(delRoomNode[0]);
获取SpaceDrawer实例类型
type SpaceDrawerRef = InstanceType<typeof SpaceDrawer>;
// 同步移除物联树的节点
(this.$refs.spaceDrawer as SpaceDrawerRef).removeCheckNode(delRoomNode[0]);
采用高级类型InstanceType可取得vue组件实例SpaceDrawer。
vue中重新声明data中属性的类型
vue中的类型得益于vue.extend能自动推断类型,但是有时候你需要自定义类型。如下例子
export default Vue.extend({
data() {
return {
obj: {
name: '',
value: ''
}
}
},
methods: {
handleObj(type: string) {
// 这样是可以的
this.obj.name = 'xxx';
// 这样是不行的,(比如type就是name)
this.obj[type] = 'xxx'
}
}
})
如上两种情况,第二种显然需要重新定义类型,因为这需要用可索引类型了。
我们可以对data内的字段重新定义类型,也就是类型断言。
如下:
type ObjType = { [key: string]: string; name: string; value: string }
export default Vue.extend({
data() {
return {
obj: {
name: '',
value: ''
} as ObjType,
}
},
methods: {
handleObj(type: string) {
// 这样是可以的
this.obj[type] = 'xxx'
}
}
})
vue中重新声明props的类型
跟data是差不多,只是vue的类型系统已经为我们提供了断言的类型 - PropType
用法如下:
export default Vue.extend({
props: {
spaceModel: {
type: Number as PropType<SpaceModelEnum>,
default: SpaceModelEnum.All,
},
spaceOperator: {
type: String as PropType<SpaceOperator>,
default: SpaceOperator.CREATE,
}
},
}
computed中get,set使类型推断异常
这里需要注意一下,有时候会遇到类型推断不起作用了,并且computed中存在异常。
这是因为computed的异常阻断了类型的进一步推断。
如下情况,此时已经不能推断出prop里面的属性:
解决方法就是,对这种写法的类型申明补全。
让mixins得到类型推断
让mixins得到类型推断
常规在组件中写入mixins属性,mixins中的组件类型是得不到推断的。
例子,这是一个mixins组件
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
created() {
console.log('mixin created');
},
methods: {
ok() {
console.log('this.isOk');
},
test(): boolean {
return true;
},
},
});
</script>
接下来,在组件中这么引用。
import Vue from 'vue';
import MixinTets from './mixinTest.vue';
export default Vue.extend({
name: 'TagList',
components: {},
props: {
tagList: {
type: Array,
required: true,
},
},
mixins: \[MixinTets\],
data() {
return {};
},
methods: {
handleClickTag(item: any) {
this.t
this.$emit('handleClickTag', item);
},
},
})
我们发现并不能正确的推导组件的实例(这里应该有test和ok方法)。
那我们怎么解决这样的问题呢?
首先,我们需要提供一个类型推断,这里没看懂没关系,他只是对vue实例中的mixins方法做了类型推论。
- 推断传入单个vue实例
- 推断传入多个vue实例,做下类型交叉。
/\* eslint-disable max-len, import/export, no-use-before-define \*/
import Vue, { VueConstructor } from 'vue';
export default function mixins<T extends VueConstructor\[\]>(
...args: T
): ExtractVue<T> extends infer V ? (V extends Vue ? VueConstructor<V> : never) : never;
export default function mixins<T extends Vue>(...args: VueConstructor\[\]): VueConstructor<T>;
export default function mixins(...args: VueConstructor\[\]): VueConstructor {
return Vue.extend({ mixins: args });
}
/\*\*
\* Returns the instance type from a VueConstructor
\* Useful for adding types when using mixins().extend()
\*/
export type ExtractVue<T extends VueConstructor | VueConstructor\[\]> = T extends (infer U)\[\]
? UnionToIntersection<U extends VueConstructor<infer V> ? V : never>
: T extends VueConstructor<infer V>
? V
: never;
type UnionToIntersection<U> = (U extends any
? (k: U) => void
: never) extends (k: infer I) => void
? I
: nev
根据以上的代码可以写出如下结构,可得类型推断。
import Mixins from 'utils/mixin';
import MixinTets from './mixinTest.vue';
export default Mixins(MixinTets).extend({
name: 'TagList',
components: {},
props: {
tagList: {
type: Array,
required: true,
},
},
data() {
return {};
},
methods: {
handleClickTag(item: any) {
this.$emit('handleClickTag', item);
},
},
})
如何写多个mixin?
枚举让代码更清晰
相信大家都知道,枚举有两种。
- 一种是常量枚举
- 一种是普通枚举
// 1、常量枚举
const enum State { SUCCESS = 0, FAIL }
// 2、枚举
enum State { SUCCESS = 0, FAIL }
那什么时候用常量枚举,什么时候用普通枚举。
你只需要知道常量枚举在js运行时是ts编译后的值。
什么意思?
如果你把常量枚举当成一个常量是不行的,因为它并不存在。
enum SpaceModelEnum {
All = 0,
ONLY_BUILDING_ROOM,
ONLY_FLOOR_ROOM,
ONLY_ROOM,
}
const enum SpaceType {
BUILDING = 0,
FLOOR,
ROOM,
}
export default Vue.extend({
name: 'SpaceCreate',
data() {
return {
// 可以
SpaceModelEnum,
// 不行
SpaceType
};
},
})
枚举用处很大,可以大大提高代码的可读性,当然用map也是能接受的。
switch (level) {
case SpaceType.BUILDING:
break;
case SpaceType.FLOOR:
parentNode = spaceDestruct.genBuildingNode();
spaceTree.push(parentNode);
break;
case SpaceType.ROOM:
const buildingNode = spaceDestruct.genAll();
parentNode = buildingNode.children[0];
spaceTree.push(buildingNode);
default:
break;
}
这里可以很清楚的知道level可能是building,floor,room类型。而不是0,1,2这种可读性很低的代码。
ts常规用法
常用的ts高级类型
- Record - 经常用的,便捷定义可索引类型,Record<string, { name: string, value: string }>
- ReturnType - 获得函数的返回类型
- ConstructParameter - 获取构造函数参数类型
- typeof - 取得对象的类型等
- keyof - 获取接口类型的key,组合为联合类型
- in - 枚举联合类型
一个优秀的ts高级类型开源库:
ts-toolbelt
一篇关于高级类型的博文:
【速查手册】TypeScript 高级类型 cheat sheet
使用泛型动态推断api接口返回的数据类型
不同的请求接口,返回不同的数据类型,那么我们如何能够定义这种类型呢?我们很容易想到用泛型。
但是每个接口都会返回固定的格式:
{
code: number;
msg: string | null;
result: any;
}
那么我们第一步需要把通用的格式抽出来,接下来result就是通过泛型去动态定义了。
这是我封装后的例子。
type ResType<T> = {
code: number;
msg: string | null;
result: T;
};
api<T>(urlProp: string, params: Record<string, any>, config = {}): Promise<ResType<T>> {
const { get: urlGet, post: urlPost } = this.urlObject;
let url = urlGet[urlProp];
if (url && url.length > 0) {
return ajax.get<T>(url, params, config);
}
url = urlPost[urlProp];
return ajax.post<T>(url, params, config);
}
const ajax = {
post<T>(
url: string,
params: Record<string, any>,
config: AxiosRequestConfig = {}
): Promise<ResType<T>> {
return new Promise((resolve, reject) => {
axios.post(url, params, config).then(
(res: AxiosResponse) => {
const { data = {} } = res;
resolve(data);
},
(err) => {
reject(err);
}
);
});
},
};
如何使用呢?
type HotelSpaceNode = {
roomId: string;
name: string;
parentId: string;
}
api<HotelSpaceNode[]>('http://debug.aqara.com/getHotelSpaceNode', {}).then(({ code, result }) => {
if (code === 0) {
// 此时result的类型就是HotelSpaceNode[]
this.spaceTreeData = result;
}
});
类型断言(强制类型转换)
这里有个例子。
比如我定义了空对象,那么我需要往对象中动态加入属性,但是这个对象依赖于某个接口。
怎么做呢?
type mapNodeType = {
spaceName: string;
spaceId: string;
spaceType: number;
parentId: string;
}
const mapNode = <mapNodeType>{};
const {
level,
childrenList,
roomId,
name,
parentId,
state,
spaceState
} = currentNode;
mapNode.spaceId = roomId;
mapNode.spaceName = name;
mapNode.spaceType = level;
mapNode.parentId = parentId;
当然上面的例子用类型声明更好一点,不管怎么样,都能提供类型推断和更高的代码可读性。
还有一种是需要赋予默认值的时候。
genFloorNode(spaceNode: FloorSpaceNode = <FloorSpaceNode>{}) {
const theSpaceNode = spaceNode;
if (_.isEmpty(spaceNode)) {
theSpaceNode.spaceType = SpaceType.FLOOR;
}
return new FloorSpaceNode(theSpaceNode);
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。