准备工作
新建一个基于ts的vue项目
通过官方脚手架构建安装
# 1. 如果没有安装 Vue CLI 就先安装
npm install --global @vue/cli
最新的Vue CLI工具允许开发者 使用 TypeScript 集成环境 创建新项目。
只需运行vue create my-app
然后选择选项,箭头键选择 Manually select features,确保选择了 TypeScript 和 Babel 选项
在已存在项目中安装typescript
在建好的vue项目中执行
vue add @vue/typescript
和之前js构建的项目结构没有什么不同,主要是js后缀变成了ts后缀,还多了tsconfig.json
、shims-tsx.d.ts
、shims-vue.d.ts
这几个文件,这几个文件的主要功能如下:
tsconfig.json
: typescript配置文件,主要用于指定待编译的文件和定义编译选项
shims-tsx.d.ts
: 允许.tsx 结尾的文件,在 Vue 项目中编写 jsx 代码
shims-vue.d.ts
: 主要用于 TypeScript 识别.vue 文件,Ts 默认并不支持导入 vue 文件
TS特点
- 类型注解、类型检测
- 类
- 接口
- 泛类
- 装饰器
- 类型声明
类型注解和编译时类型检查
使用类型注解约束变量类型,编译器可以做静态类型检查,使程序更加健壮
类型基础
// 类型注解
let var1: string;
var1 = 'Jenney'
// var1 = 1 // no ok
// 类型推断
let var2 = true;
// var2 = 1 // no ok
// 常见原始类型: string,number,boolean,undefined,null,symbol
// 类型数组
let arr: string[]
arr = ['tom', 'jerry']
// 任意类型any
let varAny: any;
varAny = 'tom'
varAny = 1
let arrAny: any[]
arrAny = [1, true, 'free']
// 函数中类型约束
//(第一个string代表参数必须有且是string类型,第二个string代表返回值必须有且是string类型)
function greet(person: string): string {
return 'hello, ' + person
}
const ret = greet('tom')
// void类型,常用于没有返回值的函数
function warn(): void {}
类型别名 type
使用类型别名自定义类型,以达到类型复用
// let Foobar1 : { foo: string, bar: number }
// let objType1 = {
// foo: 'fooo',
// bar: 1
// }
//以上方法类型无法复用
// 使用type定义类型别名,使用更便捷,还能复用
type Foobar = { foo: string, bar: number }
let objType: Foobar
objType = {
foo: 'fooo',
bar: 1
}
范例:
//1.使用类型别名定义Feature,types/index.ts
export type Feature = {
id: number,
name: string
}
//2.使用自定义类型,HelloWorld.vue
<template>
<div class="hello">
<li>
{{features.name}}
<!-- <li v-for="item in features" :key="item.id">{{item.name}}</li> -->
</li>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
// 导入
import { Feature } from '@/types';
@Component
export default class HelloWorld extends Vue {
@Prop() private msg!: string;
features: Feature = {id:1,name:'类型注解'}
}
</script>
<style scoped>
</style>
接口 interface(和接口别名的功能一致)
interface Barfoo {
foo: string;
bar: number;
}
类型别名和接口区别:
type vs interface 官方文档
总结:
在大部分情况下完全可以互换
1.大部分的区别是IDE(代码提示)的影响,对程序本身不影响
2.在2.7版本前不支持类型别名,在通用性来讲接口更好点,
(1)如果要写通用的库,建议用接口,
(2)但是在使用别名的时候语义的感觉和描述更强一些,所以如果是简单的功能用别名更好一下
联合类型 |
希望某个变量或参数的类型是多种类型其中之一
let union: string | number;
union = '1'; // ok
union = 1; // ok
交叉类型 &
并集
想要定义某种由多种类型合并而成的类型使用交叉类型
type First = {first: number};
type Second = {second: number};
// FirstAndSecond将同时拥有属性first和second
type FirstAndSecond = First & Second;
函数
//必填参:参数一旦声明,就要求传递,且类型需符合
function greeting(person: string): string {
return "Hello, " + person;
}
greeting('tom')
//可选参数:参数名后面加上问号,变成可选参数
function greeting(person: string, msg?: string): string {
return "Hello, " + person;
}
//默认值
function greeting(person: string, msg = ''): string {
return "Hello, " + person;
}
//函数重载
// 重载1
function watch(cb1: () => void): void;
// 重载2
function watch(cb1: () => void, cb2: (v1: any, v2: any) => void): void; // 实现
function watch(cb1: () => void, cb2?: (v1: any, v2: any) => void) {
if (cb1 && cb2) {
console.log('执行watch重载2');
} else {
console.log('执行watch重载1');
}
}
类
ts中的类和es6中大体相同,这里重点关注ts带来的访问控制等特性
// 03-class.ts
class Parent {
// 私有属性,不能在类的外部访问
private _foo = "foo";
// 保护属性,可以在子类中访问
protected bar = "bar";
// 参数属性:构造函数参数加修饰符,能够定义为成员属性
constructor(public tua = "tua") {}
// 方法也有修饰符
private someMethod() {}
// 存取器:属性方式访问,可添加额外逻辑,控制读写性
get foo() {
return this._foo;
}
set foo(val) {
this._foo = val;
}
}
范例:利用getter设置计算属性,Hello.vue
<template>
<li>特性数量:{{count}}</li>
</template>
<script lang="ts">
export default class HelloWorld extends Vue { // 定义getter作为计算属性
get count() {
return this.features.length;
}
}
</script>
泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。以此增加代码通用性。
具体看文档,此处简单说下
interface Result {
ok:0|1;
data: Feature[];
}
// 以上写法不通用
// T成了通用的,可以动态的指定类型,可以让程序的类型变的更加通用
interface Result<T> {
ok: 0 | 1;
data: T[];
}
//泛型方法
function getResult<T>(data: T): Result<T> {
return {ok:1, data};
}
getResult<string>()
泛型优点
- 函数和类可以支持多种类型,更加通用
- 不必编写多条重载,冗长联合类型,可读性好
- 灵活控制类型约束
不仅通用且能灵活控制,泛型被广泛用于通用库的编写。