TypeScript知识笔记---进阶

二.进阶

1.类型推断:

  如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。
  如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:

let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

2.联合类型:

  联合类型(Union Types)表示取值可以为多种类型中的一种,使用" | "分隔每个类型。联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型。

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
myFavoriteNumber = true;//index.ts(2,1): error TS2322: Type 'boolean' is not assignable to type 'string | number'.

3.类型别名:

  类型别名用来给一个类型起个新名字。使用 type 可以创建类型别名,类型别名常用于联合类型。

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}

  当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法。

4.字符串字面量类型:

  字符串字面量类型用来约束取值只能是某几个字符串中的一个。我们使用 type 定了一个字符串字面量类型 EventNames,它只能取三种字符串中的一种。注意,类型别名与字符串字面量类型都是使用 type 进行定义。

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
    // do something
}

handleEvent(document.getElementById('hello'), 'scroll');  // 没问题
handleEvent(document.getElementById('world'), 'dblclick'); // 报错,event 不能为 'dblclick'

5.接口:

  在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
    age: 25
};
let tom: Person = {
    name: 'Tom'
};
// index.ts(6,5): error TS2322: Type '{ name: string; }' is not assignable to type 'Person'.
let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};
// index.ts(9,5): error TS2322: Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.

可见,赋值的时候,变量的形状必须和接口的形状保持一致。

⑴可选属性:
  该属性可选,使用?标志,但仍然不允许添加未定义的属性。

interface Person {
    name: string;
    age?: number;
}

let tom: Person = {
    name: 'Tom'
};
let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};//报错,仍然不允许添加未定义的属性

⑵任意属性:
  使用 [propName: string] 定义了任意属性取 string 类型的值。一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集。

interface Person {
    name: string;
    age?: number;
    [propName: string]: string;
    //改为[propName: string]: any;即正确
    //也可以改为联合类型[propName: string]: string | number;
}

let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};
//任意属性的值允许是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以报错了

⑶只读属性:

  可以用 readonly 定义只读属性。

interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    id: 89757,
    name: 'Tom',
    gender: 'male'
};

tom.id = 9527;

// index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.

6.类:

⑴public private 和 protected:
  TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 publicprivateprotected

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的。
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问。
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的。

注:
①使用 private 修饰的属性或方法是无法直接存取的,在子类中也是不允许访问的;而如果是用 protected 修饰,则允许在子类中访问。需要注意的是,TypeScript 编译之后的代码中,并没有限制 private 属性在外部的可访问性。

class Animal {
  public name;
  private type;
  public constructor(name, type) {
    this.name = name;
    this.type = type;
  }
}

let a = new Animal('Jack', 'human');
console.log(a.name); // Jack
a.name = 'Tom';
console.log(a.name); // Tom
a.type = 'monkey';
console.log(a.type); // index.ts(9,13): error TS2341: Property 'type' is private and only accessible within class 'Animal'.

②当构造函数 constructor 修饰为 private 时,该类不允许被继承或者实例化;当构造函数 constructor 修饰为 protected 时,该类只允许被继承。

⑵参数属性和readonly:
  修饰符和 readonly 还可以使用在构造函数参数中,等同于类中定义该属性同时给该属性赋值,使代码更简洁。只读属性关键字 readonly ,只允许出现在属性声明或索引签名或构造函数中。注意如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面。

class Animal {
  // public name: string;
  public constructor(public name) {
  //public constructor(public readonly name) {
    // this.name = name;
  }
}
class Animal {
  readonly name;
  public constructor(name) {
    this.name = name;
  }
}

let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';

// index.ts(10,3): TS2540: Cannot assign to 'name' because it is a read-only property.

⑶抽象类:
  abstract 用于定义抽象类和其中的抽象方法。首先,抽象类是不允许被实例化的,其次,抽象类中的抽象方法必须被子类实现。即使是抽象方法,TypeScript 的编译结果中,仍然会存在这个类。

abstract class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
  public abstract sayHi();
}

class Cat extends Animal {
  public sayHi() {//需要实现抽象方法
    console.log(`Meow, My name is ${this.name}`);
  }
}
let a = new Animal('Jack');//不能实例化抽象类
//index.ts(9,11): error TS2511: Cannot create an instance of the abstract class 'Animal'.
let cat = new Cat('Tom');//只能实例化子类

⑷类的类型:
  给类加上 TypeScript 的类型很简单,与接口类似:

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  sayHi(): string {
    return `My name is ${this.name}`;
  }
}

let a: Animal = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack

你可能感兴趣的:(typescript前端)