0%

Typescript在vue3项目中的使用


引用某大佬一段有意思的话:

1
2
3
4
5
先有js,是各大浏览器厂商共同养的,
后有ts,是微软想分蛋糕,把浏览器网页标准纳入自己的系统,搞出来的,
但又不甘心当儿子,就说ts是js的超集,给js套上了一堆规矩,包括强类型,类定义等等。
但这个爹不会干活,干活的时候还是要编译成js,也就是他的儿子(js这个儿子才是会干活的正主),才能被浏览器执行。
于是编译过程中,整了个叫eslint的语法检查器,查验是不是合规的ts语法,语法不对就不给编译。

什么是ts?

ts是js的超集,是js的扩展语言,结合ide可以让我们再开发过程中知道变量的类型并提供联想提示

基础类型(泛型语法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259

1. Boolean 类型
let isAdult = ref<boolean>(false);

2. Number 类型
let age = ref<number>(17);

3. String 类型
// 普通字符串
let name = ref<string>("jeasu");

// 带变量的字符串
let msg = ref<string>(`my name is ${name.value}`);

// 拼接的字符串
let sentence = ref<string>("Hello, my name is " + name + ".\n\n" + "I'll be " + (age + 1) + " years old next month.");

4. Array 类型
let list: Array<number> = [1, 2, 3];
// Array<number>泛型语法(数组泛型)
// ES5:var list = [1,2,3];2.5 Enum 类型

let hobbies = ref<Array<string>>(["历史", "地理", "生物"]);
let list4 = ref<Array<number | string>>(['dasahk',10])

5. 联合类型
一个变量定义可能的多种类型

// 联合类型
let collection1 = ref<number | string | boolean>(6);
let collection2 = ref<number | string | boolean>("good");
let collection3 = ref<number | string | boolean>(true);

// 联合类型数组
let collection4 = ref<(number | string | boolean)[]>(["hasd", true, 36]);

//联合类型的注意点:对联合类型的数据的操作
const fn = (str: string | number): number => {
return str.length; //红波浪线报错,原因是number 没有length属性
};
fn("hello");

复杂类型

//复杂类型可以定义interface等,然后再写进泛型中
interface IUser { name: string; age: number;}
const user = ref<IUser>({ name: '林三心', age: 20})

//reactive一般用来定义引用类型,比如对象、数组
const user = reactive<IUser>({ name: '林三心', age: 20})
const users = reactive<IUser[]>([ { name: '林三心', age: 20 }])

//computed同样使用泛型
const text = computed<string>(() => '哈哈哈')
const users = computed<IUser[]>(() => ([ { name: '林三心', age: 20}]))

//defineProps
interface IProps { name: string; age?: number}
const { name, age } = defineProps<IProps>()

//defineEmits
interface IEmits { handleChange: (val: string) => void;
handleSwitch: (val: string) => void}
const emits = defineEmits<IEmits>()





6. 元组 Tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为string和number类型的元组。

// Declare a tuple type
let arr: [string, number];
// Initialize it
arr = ['hello', 10]; // OK
// Initialize it incorrectly
arr = [10, 'hello']; // Error

console.log(arr[0].substr(1)); // OK
console.log(arr[1].substr(1)); // Error, 'number' does not have 'substr'

arr[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型

console.log(arr[5].toString()); // OK, 'string' 和 'number' 都有 toString

arr[6] = true; // Error, 布尔不是(string | number)类型


let arr1 = ref<[number, string]>([8, "haode"]);
// let arr1 = ref<[number, string]>(["haode", 10]); //ERROR

7. 枚举 enum
enum类型是对JavaScript标准数据类型的一个补充

enum Color {Red, Green, Blue};
let c: Color = Color.Green;

默认情况下,从0开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1开始编号:

enum Color {Red = 1, Green, Blue}; //或者enum Color {Red = 1, Green = 2, Blue = 4};
let colorName: string = Color[2];



//枚举类型
enum Color {red,blue,green}

// 取索引
let c11 = ref<Color>(Color.blue); // 1
// 取内容
let c21 = ref<string>(Color[2]); // green

//枚举默认值,默认值可以为小数
enum Color2 {red = 1.5,blue = 10,green = 20}
let c111 = ref<Color2>(Color2.blue); // 10
let c222 = ref<string>(Color2[10]); // blue 根据默认值取对应内容
//由此可见:
enum Color {red,blue,green} //=>{red=0,blue=1,green=2}

//若是只有一个添加默认值如下
enum Color3 {red,blue = 5,green}
let c333 = ref<Color3>(Color3.red); //0 未加默认值,默认从0 开始
let c444 = ref<Color3>(Color3.blue); //5 //默认值5
let c555 = ref<Color3>(Color3.green); //6 //从默认值开始+1
let c666 = ref<string>(Color3[6]); //green
//取内容,根据默认值
let c555: string = Color3[6]; //green


8. 任意值 any
表示允许赋值为任意类型,不希望类型检查器对(用户输入或第三方代码库)这些值进行检查而是直接让它们通过编译阶段的检查,可以使用 any类型来标记这些变量

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

当你只知道一部分数据的类型时,any类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:

let list: any[] = [1, true, "free"];

list[1] = 100;

//any 类型,则允许被赋值为任意类型。
let sim = ref<any>("seven");
sim = 7;
console.log(sim, "任意类型"); //7

//变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型
let a3;
a3 = 'seven';
a3 = 7;
//相当于
let a3:any;
a3 = 'seven';
a3 = 7;

//any定义多元数组
let arr = reactive<any[]>([1, true, "hellow"]);
arr[1] = 100;
console.log(arr, "any定义多元数组"); // [1,100,'hellow']


9. 空值 void
当一个函数没有返回值时,你通常会见到其返回值类型是 void

function warnUser(): void {
alert("This is my warning message");
}

声明一个void类型的变量没有什么大用,因为你只能为它赋予undefinednull

let unusable: void = undefined;


// 声明一个void类型的变量没什么大用,只能赋予undefined和 null两种值
let unusable: void = undefined;

//但是我们通常在定义函数的时候用它,当一个函数没有返回值时,你通常会见到其返回值类型是void
const warnUser =(x:number,y:number): void=> {
console.log("This is my warning message");
if (x > y) {
console.log("x>y");
} else {
console.log("x<y");
}
}


10. Null 和 Undefined
undefinednull 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量:

// 这样不会报错
let num: number = undefined;
// 这样也不会报错
let u: undefined;
let num: number = u;

void 类型的变量不能赋值给 number 类型的变量:

let u: void;
let num: number = u;

// Type 'void' is not assignable to type 'number'.

11. Never
never类型表示的是那些永不存在的值的类型

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}

12. Object 类型
object 表示非原始类型,也就是除number,string,boolean,symbol,null或者undefined之外的类型,使用object类型,就可以更好的表示想Object.create这样的API

const create = (o: object | null): void => {};

create({ prop: 0 }); // OK
create(null); // OK

// create(42); // Error
// create("string"); // Error
// create(false); // Error
// create(undefined); // Error



类型断言

有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。

类型断言有两种形式。 其一是“尖括号”语法:

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;

另一个为as语法:

let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;
两种形式是等价的。 至于使用哪个大多数情况下是凭个人喜好;然而,当你在TypeScript里使用JSX时,只有 as语法断言是被允许的。

类型别名 type

1
2
3
4
5
6
7
8
9
10
11
12
type abc = string | number[];
type n = number;

let n1: abc;
n1 = "4";
n1 = [1];

const fn1 = (str: abc): n => {
return str.length;
};
fn1("a");

对象类型(引入概念:接口 Interfaces)

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象。接口(Interfaces)可以用于对「对象的形状(Shape)」进行描述。
注意:顺序可以乱,但是定义的对象要受到接口的约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//定义对象属性
interface defineObj {
readonly name: string; //只读属性
age: number; //必填
sex?: string; //选填
call(): string; //有返回值,类型为string
action(): void; //必填、无返回值
[propName: string]: any; //任意属性
//需要注意的是,一旦定义了任意属性,那么确定属性和可选属性都必须是它的子属性
}
const obj = reactive<defineObj>({
name: "abc", //只读,不可修改
age: 18, //sex:'男',
a: "a",
b: 9,
c: true,
call: () => "call",
action: function () {
console.log("void接口");
},
o: { id: 1 },
});
console.log(obj.call()); //call
obj.action(); //void接口
return {
obj,
};

函数类型

函数类型的定义有两点:参数类型、返回值类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//有返回值
const f1 = (x: number, y: number): number => {
return x + y;
};
console.log(f1(1, 3)); //4

//无返回值
const f2 = (n1: number, n2: number): void => {
if (n1 > n2) {
//处理逻辑
console.log(n1 + ">" + n2);
} else {
//处理逻辑
console.log(n1 + "<" + n2);
}
};
f2(9, 7); //9>7

//有返回值,比较数据
const f3 = (n1: number, n2: number): boolean => {
if (n1 > n2) {
return true;
} else {
return false;
}
};
console.log(f3(1, 2)); // false

//组合数据
const f4 = (n1: string, n2: string): string[] => {
return [n1, n2];
};
console.log(f4("a", "b")); // ['a','b']

//数据判断校验
const f5 = (s1: string, s2: string): boolean => {
let i = s1.search(s2);
return i > -1;
};
console.log(f5("abc", "a")); // true

// 函数默认值
const f6 = (x: number = 1, y: number = 2): number => {
return x + y;
};
console.log(f6()); //3

//可选参数,y可以传,也可以不传,可选参数后面是不允许出现必填参数,只能放在最后
const f7 = (x: number = 1, y?: number, z?: number): number => {
// return x + y; //报错,y为可选参数,不能加减或拼接
return x + (y! ?? 0) + (z! ?? 0);
};
console.log(f7()); // 1

接口 Interfaces

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
接口在函数中的运用
当传入的参数是对象类型的时候:

//不用接口定义时
const fn = (obj: { id: number; name: string }): string => {
return obj.name;
};
const oh = reactive({ id: 3, name: "jeasu" });
console.log(fn(oh)); //jeasu

//仅对函数入参做约束
interface params {
id: number;
name: string;
age: number;
}
const fn1 = (o: params): number => {
return o.age;
};
const oh1 = reactive({ id: 3, name: "jeasu", age: 18 });
console.log(fn1(oh1)); //18

//对整个函数的类型检查,建议对返回值类型也要定义
iinterface SearchFun {
(a: string, b: string): boolean;
}
const fn2: SearchFun = (s1, s2) => {
let i = s1.search(s2);
return i !== -1;
// return s1;
};
console.log(fn2("dsdahjk", "jk")); // true


接口继承接口

// 二维坐标接口
interface TwoDPoint{
x: number,
y: number
}
//三维坐标中的z坐标接口
interface ThreeDPoint{
z: number
}

//四维坐标接口继承了二维坐标接口的x,y坐标和三维接口的z坐标
interface FourDPoint extends ThreeDPoint, TwoDPoint{
//内还定义了四维坐标独有的时间坐标
time: Date
}
//实例化四维坐标接口
const poi2 = reactive<FourDPoint>({
z: 100,
x: 200,
y: 300,
time: new Date(),
});
console.log(poi2, "poi2");//Proxy对象{{z: 100,x: 200,y: 300,time: Mon Oct 11 2021 15:29:15 GMT+0800 (中国标准时间)}}

泛型 Generics

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
写法是 <变量类型>
泛型的产生条件:定义相同结构的函数时,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    // 如果根据业务需要传入不同类型的数据,就需要一个类型一个类型的写
// 这样容易造成代码冗余
function fnn(x:number,y:number):number[]{
return [x,y]
}

function fnn(x:string,y:string):string[]{
return [x,y]
}

function fnn(x:boolean,y:boolean):boolean[]{
return [x,y]
}

因此我们可以将类型用一个变量替代,在使用的时候,将变量类型传过去。

①、泛型变量
泛型变量 T ,T 表示任何类型,也可以用其他字母代替
因此我们上面的代码可以写成:

//函数声明方式
function declaration<T>(x: T, y: T): T[] {
return [x, y];
}

//函数表达式方式
const expression = <T>(n1: T, n2: T): T[] => {
return [n1, n2];
};
// 单类型
console.log(declaration<string>("1", "2")); //['1','2']
console.log(expression<boolean>(true, false)); //[true,false]
console.log(expression<number>(6, 7)); //[6,7]

//联合类型
console.log(expression<number | string>(6, "a")); //[6,"a"]

//当我们不给传类型的时候,类型推断编译器会自动的帮我们判断传入的是什么类型,此时传入的数据只能为单一类型
console.log(expression(1, 23)); //[1,23]

//泛型的约束
//错误示范 求变量的长度
// let variable1 = <T>(str: T): number => {
// return str.length; // T是变量,未指定类型,未必会有length这个属性
// };
//修改:
// 给参数限制类型
let limit1 = <T>(str: string | T[]): number => {
return str.length;
};
console.log(limit1<number>([1, 3])); //2

//或者给泛型变量添加约束 extends
let limit2 = <T extends String>(arr: T): number => {
return arr.length;
};
console.log(limit2<string>("one")); //3


//泛型的接口约束:就是让这个变量必须有length属性,此时就限定了变量类型有length属性,只能为string ,或者数组类型
interface ILengthNum {
length: number;
}
const limit3 = <T extends ILengthNum>(str: T): number => {
return str.length;
};
console.log(limit3<string>("oneworld")); //8
console.log(limit3<string[]>(["dasjd", "dhksah", "dahskdha"])); //3
console.log(limit3<number[]>([12, 456, 79, 465])); //4

//多个类型参数
const multi = <N, S>(sum: [N, S]): [S, N] => {
return [sum[1], sum[0]]; //实现的是交换数组内两项的位置
};
console.log(multi<number, string>([1, "one"])); //["one",1]

②泛型接口

//泛型接口
interface genface1 {
<T>(a: T, b: T): boolean;
}
const func1: genface1 = (x, y) => {
return x == y;
};
console.log(func1<number>(1111, 5)); //false
console.log(func1<string>("abc", "abc")); //true

//另一种 把泛型参数提前放在接口名上
interface genface2<T> {
(a: T, b: T): boolean;
}
const func2: genface2<number> = (x, y) => {
return x == y;
};
console.log(func2(7, 5)); //false

//另一种写法,先定义类型,再赋值函数
let func3: genface2<string>;
func3 = (x, y) => {
return x == y;
};
console.log(func3("abc", "abc")); //true

//多类型泛型接口
interface createA3<N, T> {
(a: N, b: T): Array<T>;
}
let func4: createA3<number, string>;
func4 = function (i, s) {
let arr: string[] = [];
arr[i] = s;
return arr;
};
func4(1, "dqwy");

//泛型约束
interface Length4 {
length: number;
}
interface createA4<N, T extends Length4> {
(a: N, b: T): string;
}
let func5: createA4<number, string>;
func5 = function (i, s) {
//var arr:string[] = []
//return arr
return s;
};
func5(2, "dqwy");

③用泛型变量和any的区别

//使用泛型变量 T
function fun6<T>(x: T, y: T): T[] {
return [x, y];
}
fun6<string>("a", "b");

//使用 any:缺点就是传入的类型和返回的类型不确定
function fun7(x: any, y: any): any[] {
return [x, y];
}
fun7("a", "b");

Class类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
    ES5:构造函数方式
传统的JavaScript程序使用函数和基于原型的继承来创建可重用的组件,但对于熟悉使用面向对象方式的程序员来讲就有些棘手,因为他们用的是基于类的继承并且对象是由类构建出来的。

function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.type = '动物'
Cat.prototype.eat = function(){
console.log('吃');
}
var c1 = new Cat('tom','blue')


ES6:Class 类定义
ES 6开始,JavaScript程序员将能够使用基于类的面向对象的方式。

class Cat1 {
name: string;
color: string; //属性
constructor(name: string, color: string) {
//构造函数
this.name = name;
this.color = color;
}
eat() {
//方法
console.log("吃");
}
}
const cat1 = new Cat1("tom", "blue");
console.log(cat1.name); //tom
console.log(cat1.color); // blue
cat1.eat(); //吃

类的继承

class Animal {
type: string;
constructor(type: string) {
this.type = type;
}
eat() {
return "吃";
}
say() {
return "叫";
}
}
class Dog extends Animal {
name: string;
age: number; //属性
constructor(type: string, name: string, age: number) {
//构造函数
//Animal.prototype.constructor.call(this)
super(type); //继承父类的属性和方法
this.name = name;
this.age = age;
}
action() {
console.log("hsgdajsd");
}
}
//实例化
let d = new Dog("犬", "tom", 2);
console.log(d.type); // 犬
console.log(d.name); //tom
console.log(d.age); //2
// d.eat();
// d.say();
d.action(); //hsgdajsd


1

1

1

------ The End ------
您的认可是我不断进步的动力!