认识 TypeScript

794 阅读5分钟

TypeScript.png

TypeScript

编程语言的类型

一、TypeScript 究竟是什么

  • JavaScript that scales(可扩展的 JavaScript)
  • 静态类型风格的类型系统
  • 从 es6 到 es10 甚至是 esnext 的语法支持
  • 兼容各种浏览器,各种系统,各种服务器,完全开源

二、为什么要使用TypeScript

1. 程序更容易理解

  • 问题:函数或者方法输入输出的参数类型,外部条件等
  • 动态语言的约束:需要手动调试等过程
  • 有了 TypeScript: 代码本身就可以回答上诉问题

2. 效率更高

  • 在不同的代码快和定义中跳转
  • 代码自动补全
  • 丰富的接口提示

3. 更少的错误

  • 编译期间能够发现大部分错误
  • 杜绝一些比较常见错误

4. 非常好的包容性

  • 完全兼容 JavaScript
  • 第三方库可以单独编写类型文件
  • 大多数项目都支持TypeScript

三、环境搭建

1. 安装

npm install typescript -g
tsc -V

2. 运行

// 将 ts 转换成 js
tsc demo.ts
// 执行 js
tsc demo.js
npm install -g ts-node
// 直接执行 ts
ts-node demo.ts

四、基础语法

1. 原始数据类型和 Any 类型

数据类型

JS数据类型

undefined与null的区别

let isDone: boolean = false;

let firstName: string = 'viking';
let message: string = `Hello, ${ firstName }`;

let age: number = 10;

let u: undefined = undefined;
let n: null = null;

let num: number = undefined;

let Person: {
  name: string,
  age: number
} = {
  name: 'kstring',
  age: 19
}

ts 声明类型后不能赋值其他的类型,否则会报错

ts类型错误提示

any

任意类型,可以任意调用方法、属性和赋值其他类型,丧失类型检查的作用,在有明确的类型是不建议使用,以免引起不必要的错误

let notSure: any = 4;
notSure = 'string';
notSure = true;

notSure.myName;
notSure.getName();

2. 数组和元组

let arrOfNumbers: number[] = [1, 2, 3];

元组: 一定成度上限制了数据类型的数组

// 元素不能多也不能少
let user: [string, number] = ['kstring', 19];

// 拥有数组方法,但只能添加元组中定义的类型,否则会报错
user.push('king'); // [ 'kstring', 20, 'king' ]
user.shift(); // [ 20, 'kstring' ]

// 二维元组 
const teacherList: [string, string, number][] = [
  ['dell', 'male', 19],
  ['sun', 'female', 26],
  ['jen', 'fem', 38]
]

3. Interface 接口

  • 对对象的形状 (shape) 进行描述

  • Duck Typing (鸭子类型)

    • 对象类型推断的策略,更关注对象如何被使用,而不是对象本身
// 定义接口
interface Person {
  // 只读属性
  readonly id: number,
  name: string,
  // 可选属性,
  age?: number,
}

let Person: Person = {
  id: 2,
  name: 'kstring',
  age: 19,
}

4. Function 函数

  • JS 中,函数是一等公民
    • 可以存入数组,作为参数,也可以作为返回值
// 可选参数后不能参加确定参数,与对象的可选属性不同
const add = (x: number, y: number, z?: number):number => {
  if (z) {
    return x + y + z;
  }
  return x + y;
}
// 注意:这里 =>  是在声明返回值类型,并不是 ES6 中的箭头函数
let add2: (x: number, y: number, z) => number = add

对象类型注解

function add ({ x, y }: { x: number, y: number }) {
  return x + y;
}
add({ x: 1, y: 2 });

注意: 在 ts 中 :\color{#FF0000}{:} 后都是声明类型,与代码逻辑无关

interface: 描述函数

interface ISum {
  // 这里定义返回值为 : , 而非 =>
  (x: number, y: number, z): number
}

let add2: ISum = add

5. 类型推论、联合类型、类型断言和类型别名

5.1 类型推论(type inference)

类型推论:自动推导出值得类型

类型推论

5.2 联合类型(union types)

联合类型:可以指定多种的类型,但只能赋值指定的类型

let numberOrString: number | string

5.3 类型断言(type Assertion)

类型断言:可以用来手动指定一个值的类型

function getLength(input: string | number): number {
	const str = input as string;
    if (str.length) {
    	return str.length;
    } else {
    	const number = input as number;
        return number.toString().length
    }
}
function getLength(input: string | number): number {
    if ((<string>input).length) {
        return (<string>input).length;  
    } else {
      
    }
}

5.4 类型别名(type aliases)

类型别名: 类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型

type Name = string;
function sum(x: number, y: number): number {
    return x + y;
}

const sum2: (x: number, y: number) => number = sum;

type: PlusType = (x: number, y: number) => number;

const sum3 = PlusType;

6. class

  • 类(Class): 定义了一切事物的抽象特点
  • 对象(Object):类的实例
  • 面向对象(OOP)三大特性:封装、继承、多态

访问类型

  • public:修饰的属性或方法是共有的,允许在类的内外的使用
  • protected: 修饰的属性或方法是受保护的,允许在类内及继承的子类中使用
  • private: 修饰的属性或方法是私有的,允许在类内被使用
  • abstract: 抽象
  • readonly: 只读属性

6.1 封装

  • 隐藏方法细节,只提供对外的接口,意义保护或者防止代码(数据)被无意中破坏,封装原则高内聚、低耦合
    • 内聚:内聚是指一个模块内部各个部分之间的关联程度
    • 耦合:耦合指各个模块之间的关联程度
class Animal {
  // 私有属性
  private sum:number = 0;

  getSum():number {
    return this.sum;
  }

  setSum(sum) {
    this.sum = sum;
  }
}

封装

6.2 继承

  • 继承:子类可以继承父类的特征
    • 提高代码复用性
class Animal {
 name: string;
 
 constructor(name) {
   this.name = name;
 }

 run() {
   return this.name;
 }
}

class Dog extends Animal {
 bark() {
   return `${this.name} is barking`    
 }
}

const tantan = new Dog('tantan');

console.log(tantan.run()); // tantan 
console.log(tantan.bark()); // tantan is barking

6.3 多态

  • 多态:当不同的对象去完成相同的行为会产生出不同的状态。
    • 例如:猫的叫声、狗的叫声,都是在叫,但发出的声音不一样
    • 提高了代码扩展性
class Animal {
  cry() {
    console.log("叫");
  }
}

class Dog extends Animal {
  cry() {
    console.log("汪汪");    
  }
}

class Cat extends Animal {
  cry() {
    console.log("喵喵");    
  }
}

const animal = new Animal();
const dog = new Dog();
const cat = new Cat();

animal.cry(); //叫
dog.cry(); // 汪汪
cat.cry(); // 喵喵

6.4 抽象类

抽象类的作用是实现多个子类中共用的部分, 不用重复写到实现类中

抽象类无法被实例化,只能被继承

abstract class Geom {
	abstract getArea(): number;
}

抽象类无法被实例化

6.5 抽象类与接口的异同点

相同点

  1. 都不能被实例化
  2. 都能包含抽象方法.这些抽象方法用于描述系统能提供的服务,但是不必提供具体的实现
  3. 接口和抽象类是一种数据类型

不同点

  1. 在抽象类中可以为部分方法提供默认的实现,从而避免了重复实现它们.提高了代码的可重用性.这是抽象类的优势
  2. 一个类只能继承一个直接的父类(这个父类有可能是抽象类).但是一个类可以实现多个接口,这是接口的优势

7. 类和接口

  • 继承的困境
    • 一个子类只能有一个父类
  • 类可以使用 implements 来实现接口,接口可以实现多个
// 收音机
interface Radio {
  switchRadio(trigger: boolean): void;
}

// 电池
interface Battery {
  checkBatteryStatus(): void;
}

// 汽车类
class Car implements Radio {
  switchRadio(trigger: boolean) {
    
  }
}

// 手机类
class Cellphone implements Radio, Battery {  
  switchRadio(trigger: boolean) {

  }

  checkBatteryStatus(): void {
  
  }
}

8. 枚举 enums

维基百科: 在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。 [1] 是一个被命名的整型常数的集合,枚举在日常生活中很常见,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。

使用枚举类型可以允许我们定义一些带名字的常量,也可以清晰地表达意图或创建一组有区别的用例。在 TypeScript 中,支持数字的和基于字符串的枚举。枚举是对 js 标准数据类型的补充,声明一组带名字的常量 一定范围内常量,比如星期一到星期五,

推荐阅读 TypeScript 枚举类型用法示例 typescript 中文网

enum Direction {
  Up,
  Down,
  Left,
  Right
}

console.log(Direction.Up); // 0
console.log(Direction[0]); // Up

枚举前面加个 const 可以减少开销

cosnt enum Direction {
  Up,
  Down,
  Left,
  Right
}

9. 泛型 generics

泛型: 类型的参数化,就是把类型像方法的参数一样传递

function echo<T>(arg: T): T {
	return arg;
}

// 传入的布尔值返回的还是布尔值
const result = echo(true)
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}

const result2 = swap(['string', 123]);

10. 约束泛型

interface IWithLength {
	length: number
}

// 约束传递参数必须有 length 属性
function echoWithLength<T extends IWithLength>(arg: T): T {
  console.log(arg.length);
  return arg;
}

const str = echoWithLength('string');
const obj = echoWithLength({ length: 20 });
const arr = echoWithLength([1, 2, 3]);
// echoWithLength(13) // 报错

11. 泛型在类和接口中的使用

类:

class Queue<T> {
  private data = [];
  
  push(item: T) {
    return this.data.push(item);
  }

  pop(): T {
    return this.data.shift();
  }

}

const queue = new Queue<number>();
queue.push(1);
console.log(queue.pop().toFixed());

接口:

interface KeyPair<T, U> {
    key: T,
    value: U
}

let kp1: KeyPair<number, string> = { 
    key: 1,
    value: 2
}

let arr: number[] = [1, 2, 3];
let arrTwo: Array<number> = [1, 2, 3]

12. 声明文件

TypeScript 是 JavaScript 的超集,相比 JavaScript,其最关键的功能是静态类型检查 (Type Guard)。然而 JavaScript 本身是没有静态类型检查功能的,TypeScript 编译器也仅提供了 ECMAScript 标准里的标准库类型声明,只能识别 TypeScript 代码里的类型。所以对于一些第三库就需要一些声明文件, 声明文件以 .d.ts 结尾。

也可以理解为翻译文件,用来翻译 js 代码,大多数第三方库都有自己的声明文件,不需要自己书写

npm install --save jquery
// jquery 的声明文件
npm install --save @types/jquery

jQuery 声明文件