背景
Typescript 是一种由微软开发的自由与开源的编程语言。
TypeScript 设计目标是开发大型应用,然后转译成 JavaScript。
由于 TypeScript 是 JavaScript 的严格超集,任何现有的 JavaScript 程序都是合法的 TypeScript 程序。
目前有很多大型开源项目都开始使用 TypeScript,例如:Vue 3.0、React 周边、Angluar、Vscode、AntDesign、Taro 等。
安装
npm
首先,在命令行运行 npm install -g typescript 命令全局安装 typescript;
其次,在命令行通过 tsc 命令执行 typescript 相关文件;
举例:新建一个 test.ts 文件,通过在命令行运行 tsc test.ts,同一个目录下会生成一个 test.js 文件。
tsc test.ts
编辑器
例如:Vscode、sublime、webstorm 等,都有 TypeScript 插件;
通过这些 IDE 即可自动解析 .ts 文件。
用法
基础类型
- Boolean(布尔值)
- Number(数字)
- String(字符串)
- Null 与 undefined
- Object(除 boolean、number、string、symbol、undefined、null 的类型)
- Any(任何类型)
- Void(没有任何类型,通常表示无返回值的函数)
- Never(永远不存在的值类型,是任何类型的子类型,应用场景:无限循环 & 抛出异常)
- Array (数组)
- 元组(已知类型和数量的数组)
- 枚举(对标准数据的类型的补充)
// 布尔值
const sign: boolean = true;
// 数字
const age: number = 6;
// 字符串
const str: string = 'DR';
// undefined、null
const a: undefined = undefined;
const b: null = null;
// object
const obj: object = {};
// any
const age: any = '18';
// void
function eat(): void {}
// never
function drink(): never { while(true) {}; }
function drink(): never { throw new Error(''); }
// 数组
const list: number[] = [1, 2, 3];
const list: Array<number> = [1, 2, 3];
// 元组
const list: [number, string] = [1, 'DR'];
// 枚举
enum Step = { One, Two, Three }; // One = 0, Two = 1, Three = 2
enum Step = { One = 1, Two, Three }; // One = 1, Two = 2, Three = 3
类型断言
可以用来手动指定一个值的类型;
存在两种语法:<类型>值(
// 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候
// 我们只能访问此联合类型的共有属性或者方法,否则会出现报错
// 可以
getLen = (something: string|number) => {
return something.toString();
}
// 报错
getLen = (something: string|number) => {
return something.length;
}
// 解法,在某些场景下将 something 断言成 string
getLen = (something: string|number) => {
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
接口
接口是一种规范的定义,它定义了行为和动作的规范,接口起到一种限制和规范的作用;
枚举下接口包含的几种类型:
- 属性类型接口
- 函数类型接口
- 可索引类型接口
- 类类型接口
属性类型接口
对对象进行约束,对象的 Key 与 Value 要遵守接口所声明的内容;
其中包含:正常属性,可选属性、只读属性等一些特殊内容; ```typescript interface People { name: string; age?: number; // 可选属性 readonly sex: string; // 只读属性 };
const people: People = { name: ‘段然’ // 生命属性,一定要有 age: 18, // 可选属性,可有可无 sex: ‘男’ // 只读属性 };
people.sex = ‘女’; // 错误,只读属性不能修改 console.log(people.sex) // 正确
<a name="2Wa34"></a>
### 函数类型接口
对方法传入的参数以及返回值进行约束,函数的参数与返回值要遵守接口所声明的内容;
```typescript
interface People {
(name: string, age: number): string;
}
const people: People = (name: string, age: number): string => name + age;
people('段然', 18); // 正确
people('段然', '18'); // 错误
可索引类型接口
对数组和对象进行约束(不常用),数组或者对象的索引(数组即下标、对象即 Key)与内容要遵守接口所声明的内容;
interface Obj {
[index: string]: string;
};
interface Arr {
[index: number]: number;
};
const arr1: Arr = [1, 2, 3] // 正确
const arr2: Arr = ['1', '2', '3'] // 错误
const obj1: Obj = { '1': '1' } // 正确
const obj2: Obj = { '1': 1 } // 错误
const obj3: Obj = { 1: 1 } // 错误
类类型接口
对类进行约束,类需要去实现接口中的方法与属性,类的实现要遵守接口所声明的内容;
interface Animal {
name: string;
eat(): void;
};
class Dog implements Animal {
name: string;
constructor() {
this.name = 'dog'
}
eat(): void {
}
};
const dog = new Dog(); // 正确
接口间的继承
接口之间也可以相互继承,如果继承多个接口通过 “,” 分割;
interface A {
name: string;
};
interface B {
age: number;
};
inferface C extends A { // 包含 name: string 与 sex: string
sex: string;
};
inferface D extends A, B { // 包含 name: string、age: number 与 sex: string
sex: string;
};
混合类型接口
混合类型的接口可以混合属性类型接口与函数类型接口;
interface Person {
(name: string, age: number): string; // 函数类型接口;
eat(): void; // 属性类型接口
}
function person: Person {
// 函数类型接口应用
const p: Person = (name: string, age: number): string => name + age;
// 属性类型接口
p.eat = () => {};
return p;
}
const p = person();
p('段然', 18); // 正确
p.eat(); // 正确
接口继承类
接口继承了一个类时,它会继承类的成员但不包括其实现(会继承到类的 private、protected、public 成员);
class A {
name: string;
constructor() {
this.name = '段然';
}
}
interface B extends A {
age: number;
} // 此时 B 接口中存在 name: string 与 age: number 两个属性
class C implements B {
name: string;
age: number;
constructor() {
// 类 C 必须要实现接口 B 包含的成员(name 与 age)
this.name = '段然';
this.age = 18;
}
}
class D extends A implements B {
age: number;
constructor() {
// 类 D 必须要实现接口 B 包含的成员(name 与 age)
// 但由于继承自 A,已经包含了 name,只实现 age 即可
this.age = 18;
}
}
类
公共、受保护与私有修饰符
- public 公共修饰符,Typescript 中默认为 public
- 类的实例可使用类中的方法与属性
- 派生的类可使用类中的方法与属性 ```javascript class A { public name: string; public constructor(): void { this.name = ‘段然’ } public say(): void { console.log(this.name); } }
class B extends A { constructor(props) { super(props); this.say(); // 正确 } }
const a = new A(); a.name = ‘段然然’; // 正确 a.say(); // 正确
- protected 受保护修饰符
- 类的实例不可使用类中的方法与属性
- 派生的类可使用类中的方法与属性
```javascript
class A {
protected name: string;
protected constructor(): void {
this.name = '段然'
}
protected say(): void {
console.log(this.name);
}
}
class B extends A {
constructor(props) {
super(props);
this.say(); // 正确
}
}
const a = new A();
a.name = '段然然'; // 错误,类的实例不可使用类中的方法与属性
a.say(); // 错误,类的实例不可使用类中的方法与属性
- private 私有修饰符
- 类的实例不可使用类中的方法与属性
- 派生的类不可使用类中的方法与属性 ```javascript class A { private name: string; private constructor(): void { this.name = ‘段然’ } private say(): void { console.log(this.name); } }
class B extends A { constructor(props) { super(props); this.say(); // 错误,派生的类不可使用类中的方法与属性 } }
const a = new A(); a.name = ‘段然然’; // 错误,类的实例不可使用类中的方法与属性 a.say(); // 错误,类的实例不可使用类中的方法与属性
<a name="LHNn7"></a>
### 只读属性
通过 readonly 修饰符可以设置类的属性为只读属性;
```javascript
class A {
public readonly name: string;
public constructor(): void {
this.name = '段然'
}
}
const a = new A();
a.name = '段然然'; // 报错,属性只读,不可修改
console.log(a.name) // 正确
静态属性
通过 static 修饰符可以设置属性与方法为静态属性,static 设置的属性与方法是挂载在类上面的,类的实例上是不存在此属性与方法的;
class A {
static name = '段然';
static say () {
console.log(A.name);
}
};
console.log(A.name); // 正确
A.say(); // 正确
抽象类
抽象类可以做为其它派生类的基类使用,它们不可以被实例化;
abstract class A {
public readonly name: string;
public constructor(): void {
this.name = '段然'
}
}
class B extends A {} // 正确
const a = new A(); // 报错
函数
为函数定义类型
Typescript 中的函数可以定义参数类型与返回类型;
function people (name: string): void {}
people('段然'); // 接收的参数是字符串,无返回值
函数中的可选参数
在 TypeScript 里每个参数都是可选的,可传可不传;
function people (name: string, age?: number): void {}
people('段然'); // 正确,第二个参数可不传
people('段然', 1); // 正确
people('段然', '1'); // 错误,第二个参数类型传递错误
函数中的剩余参数
在 TypeScript 里可以把剩余的所有参数收集到一个变量里,达到可扩展的目的;
function people (name: string, ...hobby: string[]): void {}
people('段然'); // 正确
people('段然', '1', '2'); // 正确
people('段然', 1, 2); // 错误,剩余的参数类型传递错误
any 与 unknown
泛型
泛型存在的关键目的是在成员之间提供可扩展性的约束,这些成员可以是:函数参数、函数返回值、接口、类的成员与类的方法等,一句话总结就是:将原本在声明时的约束移动至使用时;
泛型函数
对函数的参数与返回值进行约束;
function eat<T> (food1: T, food2: T): T { return food1 + food2; }
eat<string>('meat', 'agg'); // 使用时传递真实类型
eat<number>(1, 2); // 使用时传递真实类型
泛型接口
对接口进行约束;
interface People<T> {
name: T;
};
function people<T> (name: T): People<T> {
return { name };
}
people<string>('段然'); // 使用时传递真实类型
泛型类
对类中的成员变量与方法进行约束;
class People<T> {
name: T
constructor(name: T) {
this.name = name;
}
say(): T {
return this.name;
}
};
const people = new People<string>('段然'); // 使用时传递真实类型
const people = new People<number>(111); // 使用时传递真实类型
泛型参数的约束
对泛型的参数进行一些约束;
function eat<T extends Length> (food1: T, food2: T): T { return food1 + food2; }
eat<string>('meat', 'egg'); // 正确
eat<number>(1, 2); // 错误,只有含有 length 的参数才可以被传入;
泛型传递多个参数
泛型中可以传递多个参数,通过 “,” 进行分割;
function eat<A, B> (food1: A, food2: B): A { return food1; }
eat<string, number>('meat', 1);
泛型设置默认参数
泛型可以设置默认类型的参数;
function eat<T = string> (food1: T, food2: T): T { return food1 + food2; }
eat('meat', 'egg');