Typescript 类型
Object 类型
Object 类型不单指对象,还有数组、函数。
const foo: object = function () {}; // 也可以是{} || [] const obj: {} = {}; // 只能是对象 复制代码
数组类型
const arr1: Array<number> = [1, 2, 3]; const arr2: number[] = [1, 2, 3]; function sum(...args: number[]){ return args.reduce(prev,current) => prev+current, 0) } 复制代码
元组类型
元组类型是一种特殊的数据结构,元组就是一个明确元素数量以及每个元素类型的数组,各个元素的类型可以不相同。比如在 React 中的 useState 返回的就是一个元组类型。
const tuple: [number, string] = [18, "123"]; 复制代码
枚举类型
enum Status { Draft = 2, // 这里指定了从2开始累加,表示后面的Unpublished与Published分别是3、4;如果设置的是字符串类型,则无法自增 Unpublished, Published } const post = { tag: 2, // 1 || 3, 不用枚举类型,用字面量形式 status: Status.Draft }; 复制代码
编译之后会发现一个双向键值对的对象,双向键值对可以通过键获取到值,也可以通过值获取到键。这样可以动态的根据枚举值去获取枚举的名称。
函数类型
在 Javascript 中有函数定义方式:函数声明和函数表达式。这两种方式如何进行类型约束:
// 函数声明 function fun1(a: number): string { return "fun1"; } // 函数表达式 const fun2 = function (a: number): string { return "fun2"; }; 复制代码
任意类型
由于 Javascript 是弱类型的关系,很多内置 API 本身接收任意类型的参数。Typescript 提供了 any 这种类型,其不会进行类型检查。
let foo: any = "string"; 复制代码
隐式类型推断
在 Typescript 中,如果没有明确通过类型注解去标记变量的类型,Typescript 就会根据变量的使用情况去推断便来那个类型,这个行为就叫做隐式类型推断。如果 Typescript 无法判断出类型就会标记为 any。
let foo = 1; foo = "123"; // 报错 let foo2; // 判定为any foo2 = 123; foo2 = "123"; 复制代码
类型断言
在某些情况下,Typescript 无法判断出变量的类型,而作为开发者,可以根据变量使用情况推断出类型。
类型断言可以告诉 Typescript 变量的类型是什么,但它并不是类型转换。类型断言是属于编译阶段的概念,而类型转换属于代码执行阶段的概念。
const nums = [1, 2, 3]; const res = nums.find((i) => i > 0); // 这里明显会返回一个number,但是typescript却推断出是一个number或undefined。这时就可以进行 as关键词 或<number>断言,告诉typescript res是一个number const num1 = res as number; const num2 = <number>res; // 不过当在代码中使用jsx时,这种方法会与jsx语法产生冲突 复制代码
Typescript 接口 interface
interface 可以理解为一种规范或契约。接口用来约束对象的结构。接口可以设定可选、只读成员
interface IPost { title: string; subTitle?: string; // 可选 readonly summary: string; // 只读 [Math.random()]: "2"; // 动态 } function printPost(post) { console.log(post.title); } 复制代码
Typescript 的类
类的基本使用
类的作用:描述一类具体事物的抽象特征,从代码程度上来说,用来描述一类具体对象的抽象成员。 在 ES6 以前,Javascript 都是通过函数+原型模拟实现类。ES6 开始 Javascript 中有了专门的 class,而 Typescript 中则增强了 class 的相关语法。
Typescript 相对于 ECMAScript 新增了哪些内容:
- 1.类的属性在使用之前必须类型声明
class Person { name: string; constructor(name: string, age: number) { this.name = name; // 未报错 this.age = age; // 报错类型“Person”上不存在属性“age”。 } } 复制代码
类的访问修饰符
定义成员可访问的级别
访问修饰符 | 描述 |
private | 私有属性,只能在类的内部访问 |
public | 默认值,公有属性 |
protected | 在外部不能访问,跟 private 区别在于,protected 可以被子类访问 |
而对于构造函数 constructor 的访问修饰符,默认也是 public,如果设置成了 private,那么这个类型不能被外部实例化,也不能被继承,这样只能在类的内部添加一个静态方法,在这个静态方法中创建实例;如果设置成了 protected,也不能被外部实例化,但是可以被继承。
class Person { name: string; private constructor(name: string) { this.name = name; } } const jack = new Person(); // 报错:类“Person”的构造函数是私有的,仅可在类声明中访问。 复制代码
class Person { name: string; private constructor(name: string) { this.name = name; } static create(name: string) { return new Person(name); } } const jack = Person.create("jack"); 复制代码
类的只读属性
如果有访问修饰符,则只读属性放在修饰符后面
class Person { private readonly name: string; constructor(name: string) { this.name = name; } } 复制代码
类与接口
interface EatAndRun { eat(food: string): void; run(distance: number): void; } class Person implements EatAndRun { eat(food: string): void { console.log("eat"); } run(distance: number) { console.log("run"); } } class Animal implements EatAndRun { eat(food: string): void { console.log("eat1"); } run(distance: number) { console.log("run1"); } } 复制代码
注意,实际开发中,一般是一个接口约束一个能力,一个类型实现多个接口,低耦合原则:
interface Eat { eat(food: string): void; } interface Run { run(distance: number): void; } class Person implements Eat, Run { eat(food: string): void { console.log("eat"); } run(distance: number) { console.log("run"); } } class Animal implements Eat, Run { eat(food: string): void { console.log("eat1"); } run(distance: number) { console.log("run1"); } } 复制代码
抽象类
抽象类不同于接口的是:抽象类可以包含一些具体的实现。
定义抽象类的方式:在 class 关键词之前添加 abstract。抽象类只能被继承,不能通过 new 创建实例对象。抽象类里可以定义抽象方法,抽象方法不需要方法体,当父类有这个抽象方法,子类就需要实现这个方法。
export {}; abstract class Animal { // 抽象类 eat(food: string): void { console.log("eat1"); } abstract run(distance: number): void; // 抽象方法 } class Dog extends Animal { run(distance: number): void { // 子类修正父类的抽象方法 console.log("run"); } } const d = new Dog(); 复制代码
泛型
泛型:声明时不指定具体的类型,使用时再指定。目的是极大程度的复用代码。
function createNumberArray(length: number, value: number): number[] { const arr = Array<number>(length).fill(value); return arr; } function createStringArray(length: number, value: string): string[] { const arr = Array<string>(length).fill(value); return arr; } // 使用泛型,不明确类型的地方使用T function createArray<T>(length: number, value: T): T[] { const arr = Array<T>(length).fill(value); return arr; } const res = createArray<string>(3, "foo"); 复制代码
类型声明
在开发过程中难免会用到 npm 的第三方模块,这些模块不一定都是通过 Typescript 编写的,因此其提供的成员不会有强类型的体验。比如 lodash,就需要安装类型说明模块 types/lodash。如果没有对应的声明模块,可以通过 declare 语句自定义声明。
import { camelCase } from 'lodash'; declare function camelCase(input: string): string const res = camelCase('hello typed') 复制代码
安装了 lodash,但是没有安装类型声明文件 types/lodash,因此在代码中会报错: