最近要做一个博客系统,前台打算用VUE
加Typescript
完成。之前没有写过Typescript
代码,这次借着这个项目,把Typescript
做到基本掌握。本文会从最基础的内容开始讲起,最后会结合VUE
写一些例子程序,期间遇到的各种坑也将一并记入。下面开始Typescript
的学习把!
安装Typescript
NPM 安装 TypeScript
使用国内镜像:
1
|
npm config set registry https://registry.npm.taobao.org
|
安装 typescript:
1
|
npm install -g typescript
|
安装完成后使用 tsc
命令查看版本号:
到此,证明Typescript
安装成功。
Yarn 安装 TypeScript
1
|
yarn global add typescript
|
VSCode 配置 TypeScript 自动编译
首先,在项目目录执行初始化:
执行完成后会在项目目录下生成配置文件tsconfig.json
,修改该配置文件中的"outDir": "./js"
,该选项指定编译成js
文件的输出路径。然后执行:终端
->运行任务
->tsc:监视 xxxx/tsconfig.json
。控制台成功监视后,我们再编写ts
代码时,就会同时在设定的目录下生成对应的js
代码了。注意:启动监视任务时,如果是Windows
平台,需要首先把VSCode
的默认终端设置为Power Shell
或者命令行,否则监视程序会报错。有关配置文件的详细说明,可以参考tsconfig.json 文档
语法基础
类型注解
接下来,让我们看看TypeScript
工具带来得高级功能:类型注解。
1
2
3
4
5
6
|
(() => {
function Greet(person: string, date: Date): void {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
Greet('brein', new Date());
})();
|
在函数Greet
中我们将第一个参数person
注释为了string
类型,将第二个参数date
注释为了Date
类型,同时设置了函数得返回值为void
。这样我们就告诉了编译器,我们希望第一个参数是string
类型,第二个参数是Date
类型,函数必须返回void
。那么当我们把传入参数做了修改会发生什么呢?我们变更调用,把第二个参数修改为:
1
|
Greet('brein', Date());
|
此时,编译器就会告诉我们:
1
|
类型“string”的参数不能赋给类型“Date”的参数。
|
这对于我们得的开发简直是太有用了,再也不用担心程序跑出莫名其妙的结果了。在最一开始就为我们做出了错误提示。这可以说是对JS
的一个强力升级了。为什么拿出来单独说,第一个说,就是它太强大了,太好用了。下面我们就进入基础的学习吧。
常用语法
基础类型
-
布尔值
1
2
|
let isOk: boolean = false;
isOk = true;
|
-
数字
1
2
3
4
|
let num1: number = 10; //十进制
let num2: number = 0o12; //八进制
let num3: number = 0b0101; //二进制
let num4: number = 0xab10; //十六进制
|
-
字符串
1
2
|
let name: string = 'brein'; //单引号,双引号都可以
const info: string = `My name is ${name}.`;
|
-
undefined 和 null
1
2
|
let u: undefined = undefined;
let n: null = null;
|
-
数组
有两种方式定义数组:一种是在数据类型后加上[]
;另一种是使用数组泛型:
1
2
|
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];
|
-
元组
允许表示一个已知元素数量和类型的数组,各个元素的类型不必相同,赋值时顺序不可以变:
1
2
|
let t1: [string, number] = ['hello', 10000]; //correct
let t1: [string, number] = [10000, 'hello']; //error
|
-
枚举
enum
类型是对javascript
标准数据类型的补充。使用枚举类型可以为一组数据赋予友好的名字,让程序可读性大大提高
1
2
3
4
5
6
7
|
enum Color {
Red = 1, //可以初始化第一个值,之后的值顺次+1;或者全部元素都可以手动赋值,不必连续
Green,
Blue,
}
let myColor: Color = Color.Blue;
let colorName: string = Color[2];
|
-
any
当想为那些在编程阶段不清楚类型的变量指定一个类型。这些值可能来自于动态的内容,比如来自用户的输入或第三方代码库,就可以使用any
类型。在对现有代码进行改写时,any
类型允许你在编译时可选择的包含、移除类型检查。并且,如果只知道部分类型,也可以用any
。
1
2
3
4
5
6
7
8
|
let unknow: any = 4;
unknow = 'string?';
unknow = false;
let list: any[] = [1, true, 'string'];
list[1] = 11;
//=======output=======
false[(1, 11, 'string')];
|
-
void
表示没有任何类型(与any
相反),当一个函数没有任何返回值时,就可以使用void
。
1
2
3
|
function fn(): void {
console.log('xxxxxxxxx');
}
|
-
object
主要可以表示原始类,可以使用像Object.create
这样的方法。作为函数参数时,可以接收任何对象类型的数据。
1
2
3
4
5
6
7
8
9
10
11
12
|
function fn1(object: object) {
console.log(object);
}
fn1(new String('xxxxx'));
fn1(new Number(123));
fn1(new Boolean(true));
fn1(new Date());
//========output===========
[String: 'xxxxx']
[Number: 123]
[Boolean: true]
2021-07-22T17:12:11.525Z
|
-
联合类型
表示取值、返回值等可以是多个类型中的一种。
1
2
3
|
function toString(obj: number | string): string {
return obj.toString();
}
|
这里说一句,联合类型个人认为不如使用泛型方便。
-
类型断言
通过类型断言可以告诉编译器,被断言的变量是可信的。类型断言只在编译时起作用,并不影响程序运行时。断言有两种形式:<尖括号>
语法和as
语法
1
2
3
4
5
6
7
8
9
10
11
12
|
function getLength(obj: string | number): number {
if ((<string>obj).length) {
return (obj as string).length;
} else {
return obj.toString().length;
}
}
console.log(getLength('hello world'));
console.log(getLength(1000));
//==============output===========
11;
4;
|
-
类型推断
TypeScript
会在没有名确的指定类型的时候推测出一个类型。情况一:定义变量时赋值了,推断为值对应的类型;情况二:定义变量时没有赋值,推测为any
,之后再赋值为其他类型,会被推断为最后赋值的那个类型。
1
2
3
4
5
6
7
8
|
let testVal;
testVal = 123;
testVal = 'test string';
console.log(testVal);
console.log(typeof testVal);
//==============output================
test string
string
|
接口
TypeScript
的核心之一就是对值所具有的结构进行类型检查,我们使用接口来定义对象的类型。
接口就是对象的状态(属性)和行为(方法)的抽象。接口类型的对象的属性必须与接口完全一致。下面创建一个接口,并实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
interface person {
readonly id: number; //只读属性
name: string;
age: number;
gender?: string; //可选属性
intro(): void;
}
const person1: person = {
id: 1,
name: 'brein',
age: 18,
gender: 'male',
intro: function (): void {
console.log(`My name is ${this.name}!!`);
console.log(this);
},
};
person1.intro();
|
接口可以继承其他接口,一个或者多个:
1
2
3
4
|
interface IProgramer extends person{
xxxxxx
....
}
|
函数类型
接口还可以描述函数类型:
1
2
3
4
5
6
7
8
9
|
interface joinStr {
(first: string, second: string): string;
}
const testJoin: joinStr = function (first: string, second: string): string {
return first + second;
};
console.log(testJoin('hello,', 'world!!'));
|
类的类型
类的类型可以通过接口来进行设定,并且和C#
一样,允许一个类继承多个接口。
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
|
(() => {
interface ITalk {
sayHello(): void;
}
interface IWork {
workWithTools(): void;
}
class person implements ITalk, IWork {
sayHello(): void {
console.log('hello world!!');
}
workWithTools(): void {
console.log('I can work with tools.');
}
}
const p1 = new person();
p1.sayHello();
p1.workWithTools();
})();
//=============output==============
hello world!!
I can work with tools.
|
类
面向对象编程思想。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Student {
name: string;
age: number;
gender: string;
constructor(name: string, age: number, gender: string = 'male') {
this.name = name;
this.age = age;
this.gender = gender;
}
sayHi() {
console.log(
`Hello everyone, my name is ${this.name}, my age is ${this.age}.`,
);
}
}
const stu = new Student('brein', 18);
stu.sayHi();
//============output=============
Hello everyone, my name is brein, my age is 18.
|
继承和多态
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
|
class Person {
name: string;
age: number;
gender: string;
constructor(name: string, age: number, gender: string = 'male') {
this.name = name;
this.age = age;
this.gender = gender;
}
sayHi() {
console.log(
`Hello everyone, my name is ${this.name}, my age is ${this.age}.`,
);
}
}
class Student extends Person {
constructor(name: string, age: number, gender: string) {
super(name, age, gender);
}
sayHi() {
super.sayHi();
}
}
const stu1 = new Student('brein', 18, 'male');
stu1.sayHi();
//=======output========
Hello everyone, my name is brein, my age is 18.
|
多态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Teacher extends Person {
constructor(name: string, age: number, gender: string) {
super(name, age, gender);
}
sayHi() {
super.sayHi();
}
}
const tech1 = new Teacher('snn', 18, 'female');
function SayHi(obj: Person) {
obj.sayHi();
}
SayHi(stu1);
SayHi(tech1);
const stu2: Person = new Student('xxxxx', 20, 'female');
const tech2: Person = new Student('yyyyy', 22, 'female');
stu2.sayHi();
tech2.sayHi();
|
修饰符
什么都不加,默认为 public
。可用的其他修饰符有:private
, protected
。可以作用再属性,方法,构造函数上。还有一个readonly
修饰符,只读,对于只读属性,除了声明时赋值外,只有构造函数中可以赋值。这些用法与C#
都一致,就不实际演示了。
下面说一下,如果再构造函数的参数列表中使用readonly
,那么表示该参数在类中也有一个同样的属性,在类中就不必特别写出。当然,一样是不可以被修改的。
1
2
3
4
5
6
7
8
9
10
11
12
|
class Person {
constructor(
readonly name: string,
readonly age: number,
readonly gender: string = 'male',
) {}
sayHi() {
console.log(
`Hello everyone, my name is ${this.name}, my age is ${this.age}.`,
);
}
}
|
依次类推,如果用public
, private
, protected
等进行修饰,同样会在类中生成对应的属性,根据修饰符的特性,赋予对应的访问权限。
存取器
TypeScript
支持通过getter/setter
来截取对象成员的访问,可以有效的控制对对象成员的访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Person {
constructor(public firstname: string, public lastname: string) {}
public get fullname(): string {
return this.firstname + this.lastname;
}
public set fullname(v: string) {
const fname = v.split('-');
this.firstname = fname[0];
this.lastname = fname[1];
}
}
const p1 = new Person('zhang', 'san');
console.log(p1.fullname);
p1.fullname = 'li-si';
console.log(`firstname:${p1.firstname} lastname:${p1.lastname}`);
//==========output===============
zhangsan
firstname:li lastname:si
|
静态成员
一般的属性只有在类被实例化时,才会被实例化。而静态成员并不存在于类实例中,它是一直存在与内存中的。可以通过,类名.静态成员名
的方式来访问。
抽象类
抽象列作为其他派生类的基类,不可以被实例化。不同于接口,抽象类可以包含成员的实现细节。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
abstract class Animal {
constructor() {}
sleep() {
//可以包含具体实现
console.log('sleeping...');
}
abstract cry(): void; //必须被子类实现
}
class Person extends Animal {
constructor() {
super();
}
cry() {
console.log('i can cry.');
}
}
const p1 = new Person();
p1.sleep();
p1.cry();
|
函数
封装一些重复使用的代码,在需要的时候可以直接调用。
前面都有涉及,本节略。
剩余参数
1
|
function show(str: string, ...args: string[]) {}
|
泛型
指在定义函数,接口或者类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function getArr<T>(value: T, count: number) {
let arr: T[] = [];
for (let index = 0; index < count; index++) {
arr.push(value);
}
return arr;
}
console.log(getArr(1, 6));
console.log(getArr('brein', 6));
console.log(getArr<number>(1, 6));
console.log(getArr<string>('brein', 6));
|
多个泛型参数:
1
|
function getMsg<T, V>(value1: T, value2: V) {}
|
接口泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
interface IBaseCRUD<T> {
data: Array<T>;
add(t: T): T;
get(index: number): T;
}
class userCRUD implements IBaseCRUD<string> {
data: string[] = [];
add(t: string): string {
this.data.push(t);
return t;
}
get(index: number): string {
return this.data[index];
}
}
|
泛型类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Queue<T> {
private data: T[] = [];
push = (item: T) => this.data.push(item);
pop = (): T | undefined => this.data.shift();
}
const queue1 = new Queue<number>();
queue1.push(0);
queue1.push(1);
const queue2 = new Queue<string>();
queue2.push('xxx');
queue2.push('yyy');
console.log(queue1);
console.log(queue2);
|
泛型约束
1
2
3
4
5
6
7
8
|
interface ILength {
length: number;
}
function getLength<T extends ILength>(params: T) {
return params.length;
}
console.log(getLength<string>('xxxxxxx'));
|
其他
声明文件
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全,接口等功能。
声明语句:如果需要ts
对新的语法进行检查,需要加载对应的类型说明代码,例如:
1
|
declare var jQuery: (selector: string) => any;
|
声明文件:把声明语句放到一个单独的文件(xxx.d.ts
)中,ts
会自动解析到项目中所有声明文件。
下载声明文件:以jquery
为例,执行npm install @types/jquery --save-dev
,就会把声明文件下载到node_modules
下面的@types
里面。
(全文完)
文章作者
Brein
上次更新
2021年7月20日 01时30分29秒
许可协议
转载本站文章请注明作者和出处 Brein's Blog,请勿用于任何商业用途