TypeScript 基础

98 阅读4分钟

ts 带来了什么优势

  • 可以对变量进行类型约束
  • 能在代码编辑器中有更好的代码提示
  • 能在开发过程中避免一些潜在的风险

ts 类型

type annotation;//类型注解,手动告诉ts变量的类型

type inference; //类型推断,自动分析变量类型

const number = 1; //可以自动推断出来类型

//任何为any的值均需要添加类型注解
//函数形参需要指定类型,返回值不一定需要指定
function getTotal(n1:number,n2:number):number{
  return n1 + n2;
}

const res = getTotal(1,2); //此处不需要指定


基础类型

  • number
  • string
  • boolen
  • null
  • undefined
  • void
  • symbol
  • never : 函数不会执行完会返回 never

对象类型

  • array
  • object
  • Function

数组

const nums = [1, 2, 3]; //可以自动推断 number []

const arr: (number | string)[] = [1, '2', 3]; //混合类型数组

const objArr: { name: string; age: number } = [{ name: 'jack', age: 18 }];
// 通过这种语句定义查看会比较难,可以通过type或者interface来定义类型
type User = { name: string; age: number };

interface User {
  name: string;
  age: number;
}
const objArr: User[] = [{ name: 'jack', age: 18 }];

元组 Tuple

//数组长度,类型固定时会使用元组,通常在读取excel,csv文件时会用到
//数量格式有限的数组
const info: [string, string, number] = ['jack', 'male', 18];

const infos: [string, string, number][] = [
  ['jack', 'male', 18],
  ['alice', 'female', 20],
];

interface 接口

当同一个类型需要多次使用时,多个相同的类型注解可能会导致代码逻辑冗余,可以抽离出来

interface 和 type 不同点: type可以接受一个基础类型,而interface只能为一个对象

interface Person {
  readonly name: string; //只读
  age?: number; // ?为可空操作符
}

class 类

访问类型有三种 private,protected,public

默认为public

  • public是公共成员,允许在类外被调用,是外部可访问的
  • private是私有成员,无法被外部访问
  • protected为受保护成员,允许在内部和继承的子类中使用,外部不可调用
class Person {
  private name: string;
  constructor(name: string, public weight: number, public height: number) {
    this.name = name;
  }
}

构造函数里对参数使用访问修饰符是语法糖,可以不使用this,可以被继承

子类继承必须要在ctor中添加super调用

静态属性

  • getter / setter
  • readonly
class Person(){
 constructor(pirvate _name:string){}
 get getName(){
  return this._name;
 }
 set setName(v:string){
   this._name = v;
 }
}

抽象类

  • 抽象出来的一些共有属性
  • 只能被继承,不能被实例化

与接口的区别

  • 接口和抽象类都不能被实例化
  • 接口是把共有函数,共有属性抽离出来
  • 接口只能包含方法的定义,不能提供方法的实现
  • 抽象类可以包含普通方法以及方法的实现

联合类型和类型保护

interface Bird {
  fly: boolean;
  sing: () => {};
}

interface Dog {
  fly: boolean;
  bark: () => {};
}

type Animal = Dog | Bird;

//联合类型只会取共有的属性
function train(animal: Animal) {
  //想避免错误可以使用类型断言as
  if (animal.fly) {
    //因为Bird的fly一定是true,所以说可以断言animal为Bird
    (animal as Bird).sing();
  } else {
    (animal as Dog).bark();
  }

  //也可使用in关键字语法做类型保护
  if ('sing' in animal) {
    animal.sing();
  } else {
    animal.bark();
  }
}

泛型

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

泛型在很多编程语言中都有,指泛用的类型,可以用来取代联合类型,使代码更简洁。

当然这里也可以使用 any 来替代泛型,但是使用 any 类型会导致函数可以接收任何类型的 arg 参数,但是这样就会丢失类型保护机制,以及编辑器中的智能提示,会导致 TS 无法跟踪函数里使用的类型的信息。

let output = fun<string>('string');

这样就指定了 T 是 string 类型,并做为一个参数传给函数 注意类型要使用<>括起来而不是()

简单使用时,其实没必要使用尖括号<>来明确地传入类型;TS 有类型推断,编译器会根据传入的参数自动地帮助我们确定 T 的类型,只有在复杂情况下导致无法推断出来

let output = fun('string');

我们有时候想操作某类型的一组值,并且我们知道这组值具有什么样的属性,所以会去写我们能知道的一些属性,但是 TS 编辑器不会识别类型中的属性,所以就会报错

function len<T>(arg: T) {
  return arg.length; // Error: 类型“T”上不存在属性“length”
}

为了解决这个问题,我们可以给泛型加上类型约束

interface Length {
  length: number;
}
function len<T extends Length>(arg: T) {
  return arg.length;
}

通过这种方式给类型添加上约束,就不会报错,但是也导致了另一个问题就是 T 无法代表任意的类型。

len(1); //Error: 类型“number”的参数不能赋给类型“Length”的参数。

这时候需要传入符合约束类型的值,必须包含必须的属性,以下代码就不会有错误

len([1, 2, 3]);
len({ length: 1, value: 'abc' });