前言
TypeScript是由微软开发的一个JavaScript超集,主要提供了类型系统和对ES6的支持。正如TypeScript的官网所说:
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.译:TypeScript是JavaScript的类型化超集,能够编译为纯JavaScript语言。
使用TypeScript,能够校验变量的类型,正因为能够校验变量的类型,它弥补了JavaScript的语法中,弱类型、动态类型语言的缺陷。TypeScript声明的变量是一种静态类型,在代码编译时报错,相比JavaScript的运行时报错,能够提前发现代码错误,及时纠正。
TypeScript静态类型不仅指变量的类型不可修改,同时也说明,变量中存在的方法和属性也基本确定了下来。
以下是对TypeScript语法的基础知识的梳理,包含的内容如思维导图所示。
安装
安装TypeScript
安装TypeScript之前,先安装一下node。
全局安装TypeScript
npm install typescript -g
可能这样会报没有权限的错误,这时在前面加sudo(mac为例),然后输入密码能解决这个问题。如果要安装指定版本,加@版本号。
sudo npm install [email protected] -g
新建一个ts文件
mkdir demo1.ts
里面先写点东西,以方便后面的操作。
const count11: number = 1;
console.log(count11);
然后命令行执行:
tsc demo1.ts
tsc表示把ts文件编译成js文件。随后可以看到目录中多出一个demo1.js的文件,接着执行:
node demo1.js
在控制台中能看到打印出的1,就是count11这个变量值。
至此,可以看到从编译ts文件到控制台打印输出结果,需要分两步,但这里有个更好的方法,全局安装一个叫ts-node的插件可以进行优化,直接在控制台输出结果,但无法生成编译后但js文件。
这里需要注意的是,需要全局安装ts-node,试过局部安装了一下行不通。
全局安装ts-node
sudo npm install ts-node -g
然后想要执行哪个文件,就直接:
ts-node 那个文件.ts
基础语法
静态类型
TypeScript的静态类型包括基础类型和对象类型,基础类型有number、string、void、boolean、null、undefined......对象类型有对象、数组、类、函数。
// 基础类型举例
const num1: number = 1; // number
const str1: string = 'abcd'; // string
const nothing: null = null; //null
const uf: undefined = undefined; // undefined
const right: true = true; // boolean
function func1(): void { // void,表示函数没有返回值
console.log('void');
}
console.log(num1, str1, nothing, uf, right);
func1();
// 对象类型举例
// 对象
const obj1: {
height: number,
width: number
} = {
height: 100,
width: 100
};
// 数组
const arr1: number[] = [1, 2, 3]; // 这里表示的是数组每个元素都是number类型
// 类
class Geometry {
vector: number;
face: number
}
const geom1: Geometry = new Geometry();
geom1.vector = 8;
geom1.face = 6;
// 函数
const addXY: (x: number, y: number) => number = (x, y) => {
return x + y;
}
// 上面函数等价于
const addXY = (x: number, y: number): number => {
return x + y;
}
console.log(obj1.height, obj1.width);
console.log(arr1);
console.log(geom1.vector, geom1.face);
console.log(addXY(1, 1));
类型注解与类型推断
类型注解的意思是,需要明确地写出变量的类型,而类型推断指的是不需要明确地指定变量的类型。一般能够由Typescript进行类型推断的变量不需要进行类型注解,这个最常见的就是直接像JavaScript那样声明赋值给了一个变量,那么TypeScript就可以推断这个变量是对应的类型,怎么检测到这个变量的类型呢?使用vs code可以查看。
举个例子。
1、类型注解的例子
// 类型注解
let num2: number;
num2 = 2;
如果是声明、赋值分开写,又没有类型注解,则无法做到类型推断,这时,类型是any。
// 无法进行类型推断 num2的类型显示any
let num2;
num2 = 2;
2、类型推断的例子
// 类型推断
let num3 = 133;
// 类型推断
const obj2 = {
left: 50,
top: 100,
radius: '50%'
}
以下这个例子,说明了ts无法分析变量的类型,导致变量类型成了any。所以这里是需要用类型注解的。
function addXY2(x, y) {
return x + y;
}
const totals2 = addXY2(1, 7);
使用了类型注解后,totals2变量类型确定了,而不是之前的any。
function addXY2(x: number, y: number) {
return x + y;
}
const totals2 = addXY2(1, 7);
函数相关类型
前面写过静态类型为函数类型,指的是一个函数作为变量,也就是变量是函数类型的变量。而这里的函数相关类型指的是一个函数自身,内部的返回值类型和函数参数的解构。
函数的返回值类型
函数的返回值可以是静态类型,比如number、string、boolean、类class、数组等等。
数组作为返回值
function addXY3 (x: number, y: number): number[] {
return [x + y];
}
const totals3 = addXY3(1, 8);
console.log(totals3);
string作为返回值
function addXY3 (x: number, y: number): string {
return x + y + 'px';
}
const totals3 = addXY3(1, 8);
console.log(totals3);
class作为返回值
class computedWidth {
width: number;
}
function addXY3 (x: number, y: number): computedWidth {
return {
width: x + y
};
}
const totals3 = addXY3(1, 8);
console.log(totals3);
有两个特殊的需要注意一下。
void指的是函数没有返回值,通常在写JavaScript的代码时,写的函数会存在没有返回值的情况,这时使用void便能很好地指明函数没有返回值,一旦不小心多写了return xxx; 便会报错,及时纠正。
never指的是函数永远不会执行到最后。
// never
function addXY5(x: number, y: number): never {
throw new Error();
console.log(x + y); // 这一行无法执行到
}
函数参数解构赋值
当函数的参数是一个对象的时候,需要注意参数的解构赋值。之前有写过函数的参数的类型注解,但那些都是像number、string这样的类型,而像对象类型的参数的类型注解还没有提过。
如果参数是对象类型,需要这样写。
// 函数参数解构正确写法
function addXY4({ x, y }: { x: number, y: number }): number {
return x + y;
}
const totals4 = addXY4({ x: 2, y: 7 });
而不是这样写,会报错。
// 函数参数解构错误写法
function addXY4({ x: number, y: number }): number {
return x + y;
}
const totals4 = addXY4({ x: 2, y: 7 });
数组和元组
数组
// 数组
const strArr: string[] = ['a', 'c', 'b'];
const arrAny: (number | string | boolean)[] = ['1', 2, true];
const objArr: { width: number, height: number }[] = [{ width: 100, height: 100 }];
// 使用类型别名 type
type ComputedArea = { width: number, height: number };
const objArr2: ComputedArea[] = [{ width: 50, height: 10 }];
// class的情况
class Geometry{
left: number;
top: number;
id: string;
}
const geomList1: Geometry[] = [
{
left: 100,
top: 100,
id: 'AB001'
},
new Geometry()
];
geomList1[1].left = 200;
geomList1[1].top = 200;
geomList1[1].id = 'AB002';
console.log(geomList1);
元组
//元组,与数组的区别是元素的类型按顺序指定,不可错位
const student1: [string , string , number] = ['a001', 'Tom', 23];
// 写个复杂点的
const studentList1: [string, string ,number][] = [
['a001', 'Tom', 22],
['a002', 'Jerry', 23],
['a003', 'Jane', 23]
]
console.log(studentList1);
interface接口
interface接口与type类型别名的区别
interface接口的作用是把一些共有的属性和方法提出来放到接口里面,接口可供变量使用,作为一个对象类型(对象、函数等)。interface与type类型别名的区别是,interface只能是对象类型,而type可以是其他的基础类型,不单是对象类型。
接口中的只读属性
接口可以定义只读模式的属性,使用readonly标识。
接口中允许属性可选
接口中的属性有时不是所有使用这个接口的变量都需要,这时可以在属性名与冒号之间加上一个问号“?”,这个属性将作为可选属性使用。
接口允许有其他属性
有时使用接口的变量有自己的属性,而不是接口中定义的,这时,如果在接口中加上[propName: string]: any,将表示使用这个接口的变量可以自定义自己的属性而不需要在接口中存在。
以上所列出的接口的一些特点,在以下的代码中举了些例子。
interface Person {
name: string,
age?: number,
readonly id: string,
[propName: string]: any
}
function getName (person: Person): string {
return person.name;
}
function setName (person: Person, name: string): void {
person.name = name;
}
const Jane = getName({
name: 'Jane',
age: 23,
id: '0a001',
weight: '60kg'
});
const resetJane = setName({
name: 'Jane',
id: '0a002'
}, 'Tom');
类class应用接口
class John implements Person {
name: 'abcd';
id: '0a003';
}
接口继承
// interface
interface Person {
name: string,
age?: number,
readonly id: string,
[propName: string: any
}
interface female extends Person {
tall: string
}
类的定义与继承
// 类的定义
class Geometry {
counts = 7;
getCounts() {
return this.counts;
}
}
const geom6 = new Geometry();
console.log(geom6.getCounts());
// 类的继承
class Geometry {
counts = 7;
getCounts() {
return this.counts;
}
}
class Vector2 extends Geometry {
getVectors () {
return 8;
}
}
const geom6 = new Vector2();
console.log(geom6.getCounts());
console.log(geom6.getVectors());
如果在子类里面又重写了一遍父类的方法或属性,子类的会覆盖父类的。
class Vector2 extends Geometry {
counts = 0;
getCounts() {
return this.counts;
}
getVectors () {
return 8;
}
}
如果不想子类里重写的方法、属性覆盖父类,那么可以使用super。
class Geometry {
counts = 7;
getCounts() {
return this.counts;
}
}
class Vector2 extends Geometry {
getCounts() {
return super.getCounts() + 9;
}
getVectors () {
return 8;
}
}
const geom6 = new Vector2();
console.log(geom6.getCounts());
console.log(geom6.getVectors());
类中的访问类型
public
// 类的访问类型--public
class Canvas1 {
width: string; // 默认是public
public height: string; // 可以在子类、类的外部使用
getWidth() {
return this.width;
}
}
class SubCanvas1 extends Canvas1 {
getHeight() {
console.log(this.height);
}
}
const subCanvas1 = new SubCanvas1();
subCanvas1.height = '100px';
console.log(subCanvas1.height);
// 类的访问类型--private
class Canvas1 {
private width: string; // 只允许类内使用,在子类或类的外部使用都不行
public height: string;
getWidth() {
return this.width;
}
}
// 类的访问类型--protected
class Canvas1 {
private width: string; // 只允许类内使用
public height: string;
getWidth() {
return this.width;
};
protected background: string; // 允许在类的内部及子类中访问,类外不可访问
}
class SubCanvas1 extends Canvas1 {
getHeight() {
console.log(this.height);
}
getBackground() {
console.log(this.background);
}
}
// 类的构造器
class Canvas2 {
// 传统写法
public width: string;
constructor(width: string) {
this.width = width;
}
}
const canvas2 = new Canvas2('100px');
console.log(canvas2.width);
// 类的构造器
class Canvas2 {
// 简化写法
constructor(public width: string) {}
}
const canvas2 = new Canvas2('100px');
console.log(canvas2.width);
// 子类有构造器,那么子类要手动调用一下父类的构造器,无论父类有没有写构造器
class Canvas2 {
// 简化写法
constructor (public width: string) {}
}
class SubCanvas2 extends Canvas2 {
constructor (public id: number) {
super('100px'); // 手动调用父类构造器,有参传参
}
}
const subCanvas2 = new SubCanvas2(10001);
console.log(subCanvas2);
至此,最近学的ts基础语法笔记已记录完毕,后续会对进阶的知识做补充。