1.什么是 TypeScript
TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。
2.TypeScript优点
1)TypeScript提供了强大类型系统,可以在编译阶段就发现大部分错误
3.TypeScript使用
1) 安装
npm install -g typescript
2) 编译
tsc hello.ts(文件名)
tsc 编译全部ts文件
3) 自动编译ts文件
1.初始化 tsconfig.json
tsc --init
2.下载vscode插件保存文件自动ts代码(有个小问题,一个ts文件会生成一个js文件,生成的文件过多)
TypeScript Auto Compiler
4.数据类型
1)基本类型:Number、String、Boolean、Null、Undefined、Symbol
//限制了变量的类型,后面改变变量的值如果类型不一样报错
let a: number = 123;
const e: undefined = undefined;
const f: symbol = Symbol();
2)联合类型
// 变量a 可以是number/string/boolean类型
let a: number | string | boolean =123;
function fn(x: string | number) {
// 当我们使用联合类型定义参数
// 使用参数的属性方法必须是联合类型类型公共的属性方法才行
// return x.length; // error length属性number没有
return x.toString(); // ok toString()是公共的方法
}
// a中有bc两属性
type a = {b:string}&{c:string}
3)数组、元组
1.数组
// 1) 「类型 + 方括号」表示法
const a: number[] = [1, 2, 3];
// 2) 数组泛型
const b: Array<string> = ["1", "2"];
2.元祖
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
//类型位置不能变,值个数不能多也不能少
let x: [number, string] = [1, "hello"];
4)函数
1) 函数声明
// 函数参数xy为number类型,函数返回值为number类型
function sum(x: number, y: number): number {
return x + y;
}
// 空 函数没有任何返回值
function sayHello(): void {
console.log('hello !@');
}
2) 函数表达式
// mySum2为函数类型,参数xy为number类型,返回值为number类型
let mySum2: (x: number, y: number) => number = function (
x: number,
y: number
): number {
return x + y;
};
3) 可选参数
// 我们用 ? 表示可选的参数,可选参数后面不允许再出现必需参数了
function buildName(firstName: string, lastName?: string) {}
4) 参数默认值
function buildName(firstName: string, lastName: string = "Cat") {}
5) 剩余参数
// ...rest 的方式获取函数中的剩余参数
// 注意,rest 参数只能是最后一个参数
function push(array: any[], ...items: any[]) {
items.forEach(function (item) {
array.push(item);
});
}
5)object
// 对象dataObj有两个属性name和age,对应的类型分别是string和number
// 使用逗号或分号分割都行
let dataObj: { name: string; age: number } = {
name: 'Henry',
age: 31
};
// 等价于
let dataObj = {
name: 'Henry',
age: 31
};
//简写
let dataObj: object = {
name: 'Henry',
age: 31
};
//限制key和value类型
const accessControlTypeMap: Record<number | string, string> = {
noValue:'--',
1:'单元门禁',
2:'大门门禁',
}
6)其它类型
1. Any(任意类型)
let z: any = 123;
z = "string";
// 等价于
let o; // 类型推论 --> any
o = 123;
o = "string";
2. Void(空值)
// 表示函数没有返回值
function fn(): void {}
3. never
never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。
即使 any也不可以赋值给never。通常表现为抛出异常或无法执行到终止点(例如无线循环)
// never的应用场景 抛出异常
function error(message: string): never {
throw new Error(message);
}
// 死循环
function loop(): never {
while (true) {}
}
// never类型是任何类型的子类型,也可以赋值给任何类型
let y: number;
y = (() => :never{
throw new Error('message');
})();
4.枚举
枚举(Enum)类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等。
enum Color {
red,
green,
blue,
}
// 枚举的值默认从0开始,依次增加
console.log(Color["red"]); // 0
console.log(Color["green"]); // 1
console.log(Color["blue"]); // 2
// 实现了相互赋值
console.log(Color[0]); // red
console.log(Color[1]); // green
console.log(Color[2]); // blue
==================================
enum Color1 {
red = 3,
green,
blue = 1,
}
console.log(Color1["red"]); // 3
console.log(Color1["green"]); // 4,从上一个数值加一
console.log(Color1["blue"]); // 1
7)类
1.public、protected、private修饰符
class Person {
public name: string; // 父类子类实列对象都可以访问
protected gender: string = '男'; // 父类子类可以访问,实列对象不可以访问
private age: number = 27; // 只有在父类内部可以访问
// public username: string相当于在上面写上public username: string,在构造函数写上name: string
constructor(name: string, public username: string) {
this.name = name;
this.username = username;
}
printAge(age: number) {
this.age = age;
console.log(this.age);
this.setGender(this.gender);
}
private setGender(gender: string) {
this.gender = gender;
console.log(this.gender);
}
}
2.static 修饰词
定义类的静态属性和方法(可以理解为类的私有属性,不过可以通过类名访问)
class Person {
static PI: number = 3.14;
}
console.log(Person.PI);
3.set get修饰词
用于在类外部修改和设置类的私有属性
class Person {
private _name: string = '米斯特吴';
// 设置私有属性
set setName(value: string) {
this._name = value;
}
// 获取私有属性
get getName() {
return this._name;
}
}
let person = new Person();
// 获取值
console.log(person.getName);
// 设置值,是等号的形式,不是括号的形式
person.setName = '米修在线';
4.继承extends/super
// 子类继承父类公开的(public)和受保护的(protected)属性和方法
// super可以调用父类构造函数
class Student extends Person {
constructor(name: string, username: string, studentId: number) {
super(name, username);
}
}
5.抽象类
1) 抽象类是不允许被实例化的
2) 抽象类中的抽象方法必须被子类实现
abstract class Door {
name: string;
constructor(name: string) {
this.name = name;
}
// 定义抽象方法(没有实现)
public abstract say(): void;
}
class ADoor extends Door {
constructor(name: string) {
super(name);
}
say() {
console.log("say()");
}
}
8)接口 interface
1.接口定义
interface Person {
age: number; // :号 必须要写的
sex?: string; // ?: 可选的
readonly salary: number; // 只读 不能修改
[propName: string]: any; // 可以添加任意多个,任意类型的属性
}
let person: Person = {
age: 28,
// sex: '男'
salary: 7000,
ids: [1, 5, 10]
};
// interface 可以继承 type不能继承
// type Person2 = { name: string; age: number };
2.接口继承
interface PersonInterface {
name: string;
}
// 继承只能继承一个
interface Employee extends PersonInterface {
work: string;
}
const employee: Employee = {
name: '米斯特吴',
work: '前端开发'
};
3.类实现接口
interface PersonInterface {
name: string;
}
interface StudentInterface {
id: number;
}
// 类可以实现多个接口,必须实现必要属性
class People implements PersonInterface, StudentInterface {
name: string = '米斯特吴';
id: number = 101;
}
4.接口定义函数和数组形状
接口定义函数和数组形状时,不能有其它属性,不用写{}号
interface ArrayList {
[index: number]: boolean; // 任意属性,也可以当做数组,但是不能有其它属性
}
const c: ArrayList = [true, false];
interface SearchFunc {
// 参数为string类型,返回值为boolean类型
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function (source: string, subString: string) {
return source.search(subString) !== -1;
};
5.泛型
在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
1) 在函数中使用泛型
// identify<T>当前函数支持泛型,可以通过T指定类型
function identify<T>(arg: T): T {
return arg;
}
console.log(identify<string>('string'));
2) 在接口中使用泛型
interface GenericIdentify<T> {
(arg: T): T;
}
function identify<T>(arg: T): T {
return arg;
}
// 接口实现时,如果是一个函数或一个数组可以去掉{}号
let myIdentify: GenericIdentify<number | string> = identify;
3) 在类中使用泛型
class CountNumber<T extends number> {
number1: T;
number2: T;
constructor(num1: T, num2: T) {
this.number1 = num1;
this.number2 = num2;
}
// 泛型中计算要在变量前添加+号
calcalate(): number {
return +this.number1 / +this.number2;
}
}
const countNumber = new CountNumber<number>(10, 20);
console.log(countNumber.calcalate());
4) 泛型约束
// T必须是number类型
function getLength<T extends number>(obj: T): any {
return obj;
}
// T必须是对象,并且含有length属性
function getLength<T extends { length: any }>(obj: T): any {
return obj.length;
}
6. type关键字用法
1)类型别名
type MyType = { data: number[]; myfunc: (item: number) => number[] };
let complex2: MyType = {
data: [1, 2, 34],
myfunc: function(item: number): number[] {
this.data.push(item);
return this.data;
}
};
2)字符串字面量类型
字符串字面量类型用来约束取值只能是某几个字符串中的一个。
type Mode = 'development' | 'production' | 'none';
// mode的值只能是'development' 或 'production' 或 'none'
function fn(mode: Mode) {
console.log(mode);
}
7.检查类型、类型推论、断言
1)检查类型
let checkType = 10;
// 检测的结果必须用引号包起来
if (typeof checkType == 'number') {
console.log('number');
}
2)类型推论
// 正常
let a: number = 123;
// 类型推论: 推论为number类型,相当于let b: number = 123
let b = 123;
// 类型推论: 推论为any类型
let c;
c = 123;
c = 'string';
3)断言
1. 语法
值 as 类型
<类型>值
在 tsx 语法(React 的 jsx 语法的 ts 版)中必须使用前者,即 值 as 类型。
故建议大家在使用类型断言时,统一使用 `值 as 类型` 这样的语法。
2.用途
1)将一个联合类型断言为其中一个类型
interface Cat {
name: string;
run(): void;
}
interface Fish {
name: string;
swim(): void;
}
function isFish(animal: Cat | Fish) {
if (typeof (animal as Fish).swim === "function") {
return true;
}
return false;
}
2)将一个父类断言为更加具体的子类
class ApiError extends Error {
code: number = 0;
}
class HttpError extends Error {
statusCode: number = 200;
}
function isApiError(error: Error) {
if (typeof (error as ApiError).code === "number") {
return true;
}
return false;
}
8.命名空间
类似于局部作用域,用来防止命名冲突问题
1)命名空间的定义
// 通过namespace命名一块空间,export暴露出去,外包可以通过MyMath+.的方式访问
namespace MyMath {
export const PI = 3.14;
export function sumValue(num1: number, num2: number): number {
return num1 + num2;
}
}
console.log(MyMath.sumValue(15, 10));
console.log(MyMath.PI);
2)命名空间多文件分离
//控制台输出如下命令 将circle.ts sumValue.ts app.ts文件打包成一个app.js文件输出
tsc --outfile app.js circle.ts sumValue.ts app.ts
3)多重命名空间和引入文件
1.多重命名空间
namespace MyMath {
export namespace Circle {
const PI = 3.14;
export function calcCircle(value: number) {
return value * PI;
}
}
}
console.log(MyMath.Circle.calcCircle(8));
2.引入文件
// 三个斜杆代表开始要引入文件
/// <reference path="sumValue.ts" />
9.模块化
1)暴露模块
// 分别暴露
export const PI = 3.14;
// 默认暴露
export default function sumValue(num1: number, num2: number): number {
return num1 + num2;
}
2)引入模块
// 分别暴露的引入方式
import { PI, calcCircle } from './stuff/circle';
import * as Circl from './stuff/circle';
// 统一暴露的引入方式
import sum from './stuff/sumValue';
3)模块化编译后的语法浏览器无法识别,需要在index.html文件中做以下配置
// system.js版本文2以下
<script src="https://cdn.bootcss.com/systemjs/0.21.5/system.js"></script>
System.config({
baseUrl: '/',
packages: {
'/': {
defaultExtension: 'js' //让浏览器识别js文件
}
}
});
System.import('app.js');
10.其它
1)声明文件
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
声明语句: 如果需要ts对新的语法进行检查, 需要要加载了对应的类型说明代码
declare var jQuery: (selector: string) => any;
声明文件: 把声明语句放到一个单独的文件(jQuery.d.ts)中, ts会自动解析到项目中所有声明文件
下载声明文件: npm install @types/jquery --save-dev
2)内置对象
// let b: Boolean = new Boolean(1)
// let n: Number = new Number(true)
// let s: String = new String('abc')
// let d: Date = new Date()
// let r: RegExp = /^1/
// let e: Error = new Error('error message')
// boolean应该换成Boolean
// let bb: boolean = new Boolean(2) // error
const div: HTMLElement = document.getElementById('test')
const divs: NodeList = document.querySelectorAll('div')
document.addEventListener('click', (event: MouseEvent) => {
console.dir(event.target)
})
const fragment: DocumentFragment = document.createDocumentFragment()
3)Partial
interface IUser {
name: string
age: number
department: string
}
type optional = Partial<IUser>
// optional的结果如下
type optional = {
name?: string | undefined;
age?: number | undefined;
department?: string | undefined;
}
4)Omit
Omit<K,T>类型让我们可以从另一个对象类型中剔除某些属性,并创建一个新的对象类型:
K:是对象类型名称,T:是剔除K类型中的属性名称
type UserProps = {
name?:string;
age?:number;
sex?:string;
}
// 但是我不希望有sex这个属性我就可以这么写
type NewUserProps = Omit<UserProps,'sex'>
// 等价于
type NewUserProps = {
name?:string;
age?:number;
}
5)ts类型中的&
type Env1 = 'prod' | 'test' | 'dev';
type Env2 = 'prod' | 'boe' | 'ppe';
type EnvInter = Env1 & Env2; // 'prod'
6)ts类型中的??
//a为undefind或者null时返回b
a??b