TypeScript及TypeScript在vue3.0项目中的基本使用
一、 TypeScript是什么TypeScript 是一种由微软开发的自由开源的编程语言,主要提供了类型系统和对 ES6的支持。它是JavaScript的一个超集,扩展了JavaScript的 语法,因此现有的 JavaScript 代码可与TypeScript 一起工作无需任何修改。TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScri
一、 TypeScript是什么
-
TypeScript 是一种由微软开发的自由开源的编程语言,主要提供了类型系统和对 ES6的支持。它是JavaScript的一个超集,扩展了JavaScript的 语法,因此现有的 JavaScript 代码可与TypeScript 一起工作无需任何修改。
-
TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript可以运行在任何浏览器上。 TypeScript 是 Angular2、vue3的开发语言
二、 TypeScript与JavaScript的区别
- 语言层面:JavaScript和TypeScript都是ECMAScript(ECMA-262)的具体实现。
- 执行环境层面:浏览器引擎和Node.js都能够直接运行JavaScript,但无法直接运行TypeScript。
- 时序层面:TypeScript被真正执行前,会通过编译转换生成JavaScript,之后才能被解释执行。
- 厂商层面:JavaScript由Netscape率先推出,现在主要由各大浏览器厂商实现。而TypeScript is a trademark of Microsoft Corporation,目前由微软进行设计和维护。
三、 为什么要用TypeScript
(一)类型检查,语法提示
在TS中允许你为变量指定类型。当你给已定义类型的变量赋值的时候,值的类型不对,便会有错误提示
(二)约束类型,减少不必要的代码逻辑
//定义一个函数计算二个数据的合计
function sum(x,y){
if(typeof x != 'number') { //对于形参的类型要添加转换
x = parseInt(x);
}
return x+y
};
sum('1',2);
//TS的方式,直接约束了类型
function sum2(x:number,y:number){
return x+y
};
(三)代码重构
Typescript 支持类型最大的好处是可读性。 类型可以给开发者更多的信息,是最有价值的文档。类型很好的体现了代码即文档这一思想。
四、 TypeScript开发的使用建议
是否使用ts来开发项目?或者说ts有必要吗?----- 根据开发场景,怎么方便怎么来
开发条件:
- 大型项目,代码量较多
- 进行封装,组件化开发的时候(有一定开发基础)
- 开发团队中至少一半的成员对ts要有基本的了解,至少有一个成员对ts相关知识点很熟悉
影响:
对前端来说,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等可能不是很熟悉的知识点,有一定的学习成本。
五、支持TypeScript的编辑器
TypeScript 最大的优势之一便是增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等。 主流的编辑器都支持 TypeScript,推荐使用 Visual Studio Code。
获取其他编辑器或 IDE 对 TypeScript 的支持:
- Sublime Text
- Atom
- WebStorm
- Vim
- Emacs
- Eclipse
- Visual Studio 2015
- Visual Studio 2013
- IDEA 系列
六、TypeScript的安装及 .ts 文件的编译
(一)全局安装
npm install -g typescript
以上命令会在全局环境下安装 tsc 命令,安装完成之后,我们就可以在任何地方执行 tsc 命令了。
(二)查看版本
tsc -v
(三)编译一个 .ts文件
tsc hello.ts
七、TypeScript各类型的定义
TypeScript写法
使用 :指定变量的类型,:的前后有没有空格都可以
let num:number = 15;
num(变量名):number(类型) = 15(具体值)
表示定义一个变量num,指定类型为number;
let str:string = 'abc';
表示定义一个变量str,指定类型为string;
(一) 基础类型
字符串类型 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.");
布尔类型 boolean
// 布尔类型
let isAdult = ref<boolean>(false);
数字类型 number
// 数值类型
let age = ref<number>(17);
任意值类型 any
如果是一个普通类型,在赋值过程中改变类型是不被允许的,任意值(Any)用来表示允许赋值为任意类型。
let a1 = ref<string>('seven');
a1 = 7;//error
//但如果是 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']
枚举类型 enum
//枚举类型
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
//任意值,任意值后面不能加别的类型(未赋予默认值)
enum Color4 {
red,
blue = 5,
green = <any>"abccc",
// green = <any>4, //4
// green = <any>true, //true
// yellow, //undefined
yellow = "BALUE".length,
}
let c777 = ref<Color4>(Color4.blue); //5
let c888 = ref<Color4>(Color4.green); //abccc
let c999 = ref<Color4>(Color4.yellow); //5
let c100 = ref<string>(Color4[<any>"abccc"]); //green
数组类型 array
两种方式定义数组:
第一种方式:元素类型后面加上[ ]
let list1 = ref<number[]>([1, 2, 3]);
let list2 = ref<string[]>(["1", "2", "a"]);
let list3 = ref<any[]>([1,true,'abc',{id:2}])
第二种方式是使用数组泛型,Array<元素类型>:
let hobbies = ref<Array<string>>(["历史", "地理", "生物"]);
let list4 = ref<Array<number | string>>(['dasahk',10])
联合类型
一个变量定义可能的多种类型
// 联合类型
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");
元组类型 Tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同,且数组各项类型与定义一一对应。元组的长度和类型不可变:
let arr1 = ref<[number, string]>([8, "haode"]);
// let arr1 = ref<[number, string]>(["haode", 10]); //ERROR
Void类型
它表示没有任何类型。
// 声明一个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");
}
}
Null 和Undefined
默认情况下Null 和Undefined是所有类型的子类型。 就是说你可以把 Null 和Undefined赋值给number类型的变量
// undefind类型
let u: undefined = undefined;
// null类型
let n: null = null;
Never 类型
表示永远不会存在值的类型,常用来定义抛出异常或根本就不会有返回值的函数
never 类型是任何类型的子类型,也可以赋值给任何类型;没有类型是never的子类型或者可以赋值给类型(除了never本身)
never 和 void 之间的区别是void 意味着至少要返回一个 undefined 或者 null ,而 never 意味着不会正常执行到 函数的终点。
// 返回never的函数必须存在无法达到的终点
const error = (message: string): never => {
throw new Error(message);
};
const infiniteLoop = (): never=> {
while (true) { }
}
// 推断的返回值类型为never
const fail = ()=> {
return error("Something failed");
}
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
(二) 类型断言
类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。
类型断言有两种形式。 其一是“尖括号”语法:
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;
(三) 类型别名 type
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)」进行描述。
注意:顺序可以乱,但是定义的对象要受到接口的约束
//定义对象属性
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,
};
(五) 函数类型
函数类型的定义有两点:参数类型、返回值类型
//有返回值
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、接口在函数中的运用
当传入的参数是对象类型的时候:
//不用接口定义时
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
2、接口继承接口
// 二维坐标接口
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)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
写法是 <变量类型>
泛型的产生条件:定义相同结构的函数时,如下
// 如果根据业务需要传入不同类型的数据,就需要一个类型一个类型的写
// 这样容易造成代码冗余
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]
}
因此我们可以将类型用一个变量替代,在使用的时候,将变量类型传过去。
1、泛型变量
泛型变量 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]
2、泛型接口
//泛型接口
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");
3、用泛型变量和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、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')
2、ES6:Class 类定义
从ECMAScript 2015,也就是ECMAScript 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(); //吃
3、类的继承
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
4、修饰符
static 静态方法
不需要通过实例化处理,可以直接通过类来调用
class Obj {
static str: string = "abc";
//constructor可以不写,因为上面已经给变量赋值
//constructor(str){
// this.str = str;
//};
static action() {
console.log("static 静态方法");
}
}
//不需要实例化,直接调用Obj
console.log(Obj.str); //abc
Obj.action(); //static 静态方法
public 修饰公共属性或方法
public 修饰的属性或方法都是共有的,可以在任何地方被访问到,默认所有属性和方法都是public。用下面的方式来重写 Cat2:
class Cat2 {
public name: string;
public color: string; //属性
public constructor(name: string, color: string) {
//构造函数
this.name = name;
this.color = color;
}
public action() {
console.log("public 修饰公共属性或方法");
}
}
//实例化对象再取值、调用
let cat2 = new Cat2("John", "white");
console.log(cat2.name); //John
console.log(cat2.color); //white
cat2.action(); //public 修饰公共属性或方法
private 修饰私有属性或方法
修饰的属性或方法是私有的,不能在声明它的类外部访问。我们将上例中的 action 方法修饰符改为 private:
class Cat3 {
public name: string;
private color: string; //属性
public constructor(name: string, color: string) {
//构造函数
this.name = name;
this.color = color;
}
private action() {
console.log("private 修饰私有属性或方法");
console.log(this.name); //jack ,在内部能访问到该类中的属性
}
}
//实例化对象再取值、调用
let cat3 = new Cat3("jack", "black");
console.log(cat3.name); //jack
//下面能打印值,但是有红波浪线报错
// console.log(cat3.color); //报错,说明访问不到 private 修饰的属性
// cat3.action(); //报错,说明访问不到 private 修饰的方法
protected 修饰受保护的属性或方法
修饰的属性或方法是受保护的,它和private类似,区别是它在子类中也是允许被访问的。我们将上例中的 action 方法修饰符改为 protected:
class Cat4 {
protected name: string;
protected color: string; //属性
public constructor(name: string, color: string) {
//构造函数
this.name = name;
this.color = color;
}
protected action() {
console.log("protected 修饰受保护的属性或方法");
}
}
//实例化对象再取值、调用
let cat4 = new Cat4("tom", "yellow");
//能打印出来,但是页面书写红波浪报错
// console.log(cat4.name); //说明访问不到 protected 修饰的变量
// console.log(cat4.color); //说明访问不到 protected 修饰的变量
// cat4.action(); //说明访问不到 private 修饰的方法
//创建一个子类继承Cat3
class littleCat extends Cat4 {
constructor(name: string, color: string) {
super(name, color);
this.name = name;
this.color = color;
}
say() {
//return this.name //tom 可以访问父类里面 protected 修饰的属性
console.log(this.name);
console.log(this.color);
return this.action();
}
}
const dog2 = new littleCat("tom", "hua");
dog2.say(); //tom //hua //protected 修饰受保护的属性或方法
5、类实现接口 implements实现
接口和类的区别
接口,对属性和方法类型的约束,抽象的约束
interface ObjType{
name:string;
action():string;
}
类,对属性和方法的定义,具体的内容
class Obj5{
name:string;
constructor(name){
this.name = name
}
action(){console.log('执行')}
}
回顾之前的接口定义对象:
interface ObjType{
name:string;
action():string;
}
let o6:ObjType = { //强制指定类型,添加或减少属性或方法会报错
name:'abc',
action(){return '123'}
}
类 实现 接口 (implements 实现)
interface ClassType {
name: string;
action(): void;
}
class Obj implements ClassType {
//类Obj受到接口ClassType的约束
// 类中的属性和方法要受到接口的约束,但是添加属性或方法不会报错
//属性和方法只能比接口里定义的多,不能少
name: string;
age: number; //新增属性,接口里未定义
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
action() {
console.log("执行");
}
a() {
return "sah";
} //新增方法,接口未定义
}
const obj1 = new Obj("hahaha", 18);
console.log(obj1.name); //hahaha
console.log(obj1.age); //18
console.log(obj1.a()); //sah
obj1.action(); //执行
不同类实现同一个接口
不同类之间有共同的特性,我们把这个特性提出来,就可以定义成一个接口,然后不同类中再去用 implements 去实现这个接口:
//警报功能
interface Alam {
name: string;
ring(): void;
}
//公共类 门
class Door implements Alam {
name: string;
constructor(name: string) {
this.name = name;
}
ring() {
console.log("我是门这个类");
}
}
const door = new Door("door");
door.ring(); //我是门这个类
//门的子类防盗门 拥有报警功能
class SecurityDoor extends Door implements Alam {
//里面不写任何东西,完全继承父类
// 若添加,则属性和方法是这个子类自己的
price: number;
constructor(name: string, price: number) {
super(name);
this.name = name;
this.price = price;
}
ring() {
console.log("我是安全门,门的一个字类");
}
}
const securityDoor = new SecurityDoor("door", 2889);
console.log(securityDoor.price); //2889
securityDoor.ring(); //我是安全门,门的一个字类
//车 这个类 拥有报警功能
class Car implements Alam {
name: string;
constructor(name: string) {
this.name = name;
}
ring() {
console.log("我是车这个类");
}
}
const car = new Car("door");
car.ring(); //我是车这个类
一个类实现多个接口
//警报功能
interface Alam1 {
ring(): void;
}
//开灯关灯
interface Light {
lightOn(): boolean;
lightOff(): boolean;
}
//车实现这两个功能
class Car1 implements Alam1, Light {
//用逗号链接接口名
ring() {
console.log("车的报警功能");
}
lightOn() {
return true;
}
lightOff() {
return false;
}
a() {} //只能多不能少
}
const car1 = new Car1();
car1.ring(); //车的报警功能
console.log(car1.lightOn()); //true
console.log(car1.lightOff()); //false
6、接口继承类
//定义了一个Bird类
class Control {
private state: any; //state是Control的私有属性,只能在Control中访问该变量
constructor(state: any) {
this.state = state;
}
print() {
console.log("这是Control类的打印操作");
console.log(this.state, "打印Control的私有属性state");
}
}
const control = new Control(false);
control.print();
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
// state: any;
name: string;
constructor(name: string, state: any) {
super(state);
this.name = name;
}
select() {
console.log("这是Button类的操作");
// console.log(this.state); //Control中state修饰符改为protected这个才对
}
}
const button = new Button("button", true);
console.log(button); //button中有state,但是无法访问state
// console.log(button.state); //Control中state修饰符改为public这个才对
button.select();
class TextBox extends Control {
constructor(state: any) {
super(state);
}
select() {
console.log("这是textbox的select");
}
}
const textbox = new TextBox("true");
textbox.select(); //这是textbox的select
7、泛型在类中的运用
class A2<T> {
n: T;
constructor(num: T) {
this.n = num;
}
add(x: T): T {
return x;
}
}
var a2 = new A2<number>(1);
console.log(a2.n); //1
console.log(a2.add(3)); //3
八、vue3.0项目中使用typescript
(一)项目的创建
新版本脚手架@vue/cli 创建项目的方式:vue create 项目名称
- 注意点:项目名称不能用驼峰命名法,可以用下划线链接,例如 ts_demo3.0
选择第三个手动创建
选择配置项,因为要演示ts的基本使用,所以一定要选这个
选择vue的版本
提示你是否需要使用class-style,这里不使用使用这个类样式语法,选N
是否使用typescript 和 babel 的形式编译 jsx ,这里选择N
选择路由模式,是否使用history模式
选择css 预处理语言
选择ESLint 配置
选择什么时候进行代码规则检测,保存的时候检测
选择如何存放配置,放在package.json中
是否保存当前配置,方便下次使用
项目创建完成
(二)声明变量的两种方式
1、 ref
ref 常用来监听基本类型数据的变化
<template>
<div>
<p>{{count}}</p>
<p>{{msg}}</p>
<p>{{isShow}}</p>
<button @click="myFun">按钮</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'App',
setup() {
const count = ref<number>(5)
const msg = ref<string>('这是字符串')
const isShow = ref<boolean>(true)
const myFun = ()=>{
console.log(count.value)
console.log(msg.value)
console.log(myFun.value)
}
return {
count,
msg,
isShow,
myFun
}
}
}
</script>
2、 reactive
reactive常用来定义复杂类型数据
<template>
<div>
<ul>
<li v-for="(item,index) in list"
:key="item.id"
style="cursor:pointer"
@click='remove(index)'>
{{item.name}}------{{item.age}}
</li>
</ul>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
interface listType{
id:number,
name:string,
age:number
}
const list = reactive<listType[]>([
{ id: 1, name: 'zs', age: 10 },
{ id: 2, name: 'ls', age: 20 },
{ id: 3, name: 'ww', age: 30 },
])
const remove = (index:number):listType[] => {
//点击删除当前项
list = list.filter((item, idx) => idx !== index)
}
return {
list,
remove
}
}
}
</script>
九、vue3.0+ts项目中引用第三方类库
(一)问题及解决:
在开发过程中,不可避免要引用第三方 javascript 类库。通过直接引用可以调用库的类和方法,但是却无法通过 TypeScript 的严格类型检查机制。此时我们便需要必须为它编写一个声明文件,对外暴露它的 API,有些源码包含了声明文件,有些则需要单独安装。
(二)如何判断是否需要手动添加声明文件:
安装插件/组件库后,在他的安装包里检查是否包含声明文件(后缀以 .d.ts结尾的文件),如果已经包含则不需要处理,可在项目中按正常流程使用;如果不包含声明文件,可以在 typescript 社区搜索 查询相关第三方库的声明文件并通过 npm install 下载;如果社区搜索里也没有的话,则需要自己添加声明文件
(三)三种情况的详细展示:
第一种:ts类库,自带声明文件
由typescript编写或者有些源码包含了声明文件,例如:element-plus,类似的还有axios等等
安装:
npm install element-plus --save
全局注册:
//main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus) app.mount('#app')
安装完成后:
源码里每个组件或方法都有它对应的声明文件
第二种:非ts类库,但是已经存在声明文件
当我们发现安装的依赖里面没有声明文件,例如:lodash
安装:
npm i lodash --save
安装完成后:
node_modules中lodash内没有 任何声明文件
我们先去 typescript 社区查询是否有别的贡献者已经编写好的声明文件
我们发现社区里有,并写后面附上了声明文件的安装命令
此时我们已经安装过lodash,所以只需要输入下面安装声明文件的命令:
npm i @types/lodash --save-dev
安装完成后,在node_modules中的@types目录下我们找到lodash,展开可以看到对lodash内的对象或方法做的声明
在页面中局部使用:
//引入
import _ from 'lodash'
//调用lodash
const aa = _.differenceBy([3.1, 2.2, 1.3], [4.4, 2.5], Math.floor);
第三种:非ts类库,也不存在声明文件
这时需要我们手动编写声明文件,例如:indexof
安装:
npm i indexof
安装完成后,安装包内没有声明文件:
我们再去 typescript 社区搜索 查询,没有单独的这个函数插件的声明文件:
因此我们可以手动添加一个声明文件:
1、创建声明文件目录
在src目录下创建@types文件夹,该文件夹内存放自定义声明文件,例如: indexof / indexof.d.ts
2、tsconfig.json 配置编译识别路径
在根目录下的tsconfig.json文件中,includes属性下添加:“src/**/*.d.ts” ,表示src文件夹下的所有 .d.ts文件都会被识别
3、在项目中试用
在使用之前我们一定要看看这个组件或方法的暴露方式,查看它的依赖文件,暴露方式是module.exports = ,我们取用的的时候用 require ,用 import 会报错!
代码测试,console.log(bb)
(四)其他常用插件的使用
全局使用jquery
全局挂载是vue2.x中的思想,vue3.0中已经不推荐使用这种,我们可以在vue.config.js中配置全局使用(vue2.x中也可以使用):
a、使用@vue/cli脚手架构建的项目,在根目录新建vue.config.js文件:
var webpack = require("webpack")
module.exports = {
configureWebpack: {
plugins: [
new webpack.ProvidePlugin({
jQuery: "jquery",
jquery: "jquery",
$: "jquery",
"windows.jQuery": "jquery"
})
]
}
}
b、在webpack构建的项目中,在webpack.base.conf.js文件中:
const webpack = require('webpack')
module.exports = {
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
jquery: "jquery",
"window.jQuery": "jquery"
})
]
}
配置完成后,需要在main.js中引入
import $ from 'jquery'
然后就可以直接使用了
$("#main").html("hsahashhadsahsdajsgd")
.css("background-color", "yellowgreen");
局部使用echarts
1、安装
npm install echarts --save
本次默认安装echarts版本:“^5.2.1” ,这个版本自带声明文件,所以不需要安装@types/echarts,其他版本安装 Vue+Typescript项目中使用echarts (未尝试)
2、引入
在需要引用echarts的组件script中引入echarts:
import echarts from 'echarts';
3、使用
const myChart = echarts.init(document.getElementById("myecharts"));
myChart.setOption({
xAxis: {
type: "category",
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
},
yAxis: {
type: "value",
},
series: [
{
data: [120, 200, 150, 80, 70, 110, 130],
type: "bar",
showBackground: true,
backgroundStyle: {
color: "rgba(220, 220, 220, 0.8)",
},
},
],
});
写完后,我们会发现有红波浪线报错:
Argument of type ‘HTMLElement | null’ is not assignable to parameter of type ‘HTMLElement’. Type ‘null’ is not assignable to type ‘HTMLElement’.
就是我们没有给获取到的dom加类型,有可能他是null,此时的解决办法是:添加类型断言
然后运行,结果在控制台发现报错:
原因: 在官网中我们可以看到,v5中去除了default exports 的支持,导入的写法有所改变
解决方案: 在导入包的时候加上 * as ,刷新页面,图表功能正常显示
更多推荐
所有评论(0)