非权威机构的定义
let a: number = 6; // : number即为类型注解
对代码进行了编译,使得在开发阶段就可以使用一些扩展语法。
npm install flow-bin -D
。这里使用了开发依赖来安装flow-bin包,为的是让flow跟随项目,让项目的其他开发人员了解需要使用flow来进行静态类型检查。也可以使用全局安装的形式,使用命令行来启动flow的检查。npx flow init
命令生成.flowconfig配置文件// @flow
标记或者更为复杂注解方式。// index.js文件
// @flow
/**
* 这是另一种在开头添加标记的方式
* @flow
**/
function sum(a: number, b: number) {
return a + b;
}
sum(100, 200);
// 在执行flow的时候,下面的调用会报错
sum('100', '200');
npx flow
来读取配置文件,执行flow服务。第一次执行的时候有点慢,是因为启动了一个后台服务,之后再执行flow时,就会很快的。官方的移除注解方案
npm install flow-remove-types -D
npx flow-remove-types 目标文件路径 -d 生成文件路径
npm install @babel/core @babel/cli @babel/preset-flow -D
// .babelrc文件
{
"presets": ["@babel/preset-flow"]
}
babel 目标文件路径 -d 生成文件路径
启动编译安装vscode的Flow Language Support插件,可以在vscode开发过程中进行静态类型检查,将代码中的类型问题以红色波浪线显示出来。
Flow官网给出的所有编辑器下支持的插件,点击查看
Flow也可以根据值的类型推断出存储该值的变量的类型,从而显示出类型问题。但尽量还是添加类型注解,增加代码可读性。
const a: string = 'foo';
const b: number = NaN; // NaN属性number类型
const c: boolean = false;
const d: null = null;
const e: void = undefined; // 要标注值为undefined类型时,需要用void
const f: symbol = Symbol('symbol type');
const arr1: Array = [1, 2, 3]; // 表示数组元素全部都是number类型
const arr2: number[] = [1, 2, 3];
const foo: [string, number] = ['foo', 6]; // 表示固定长度的数组,并指定元素类型,这里实际上表示了元组这种类型
const obj1: { foo: string, bar: number } = { foo: 'foo', bar: 6 } //对属性值进行类型注解
const obj21: { foo?: string, bar: number } = { bar: 6 } // 可以在属性名后添加?表示该属性属于可选属性
const obj3: { [string]: string } = {}; obj3.key1 = 'value1'; obj3.key2 = 68; // 动态添加属性的情况下,可以先指定属性的类型
function square(n: number): number {
return n * n;
}
// 没有返回值则标记为void
function foo(): void {
console.log('void function');
}
(参数类型注解) => 返回值类型注解
function foo(callback: (string, number) => void) {
callback('string', 66);
}
类型注解为某个字面量值const a: 'foo' = 'foo'; // 要求变量a只能存放值'foo';
一般这样做意义不大,主要是配合联合类型来使用
const type: 'success' | 'waring' | 'danger' = 'success'; // type变量只能存放'success'、'warning'、'danger'这三个值中的某一个
const b: string | number = 'foo'; // 变量b只能存放string类型或者number类型
当变量可以是约束的类型,也可以是null或undefined时,使用?
作为类型前缀来注解。
// ?number表示除了number类型外,可以是undefined或null
const gender: ?number = undefined;
所有类型的联合类型const a: mixed = 'foo';
。
mixed类型的意义在于变量不能在使用过程中随意更改自己的类型。一旦确定了是哪种类型,之后就必须是该类型。换句话说,mixed依然是强类型。
任意类型const b: any = 'foo';
any类型允许变量在使用过程中随意更改自己的类型,即弱类型。
使用type关键字来声明一个类型(自定义一个类型,或者称为类型别名),然后可以使用该类型别名来做注解。
// type声明一个StringOrNumber的类型别名,然后可以使用该类型别名来做注解
type StringOrNumber = string | number;
const c: StringOrNumber = 66;
官方类型手册
第三方类型手册,该手册更加直观清晰。
如浏览器环境或Node环境下提供的API,这些API所对应的类型声明文件。
这些API对应的类型声明文件点击这里。
超集 = JavaScript + 类型系统 + ES6+
TypeScript 通过编译生成 JavaScript
npm install typescript -D
作为开发依赖安装,当然也可以全局安装。局部安装之后,就会出现/node_modules/bin/tsc文件,tsc作为TypeScript编译文件的命令。npx tsc 源文件路径 -o 生成文件路径
来将源文件编译为JavaScript,将ES6+语法生成ES5甚至ES3的语法。npx tsc --init
在项目根目录下生成tsconfig.json配置文件npx tsc --locale zh-CN
命令让TypeScript在控制台显示中文错误消息。如果文件之间不是模块作用域或者函数作用域时,声明的变量会被编译到全局作用域,有可能会出现重复声明的报错问题。
基本与Flow一致。
不同点:
泛指所有的非原始类型,或者说object类型指的是所有引用类型。
const foo: object = {}; // 值可以是对象、数组、函数等等
如果单纯指对象类型,则使用对象字面量来做类型注解或者接口interface。对象类型要求值与类型注解的shape完全一致。
const obj: { foo: number } = { foo: 66 }; // 这里的值必须是属性为foo且值为number类型的对象
Array
Array泛型,如Array
[]
泛型字面量,如number[]
const tuple: [number, string] = [ 66, 'hello'];
// 声明枚举类型PostStatus
enum PostStatus {
// 注意这里使用=而不是:来赋值
Draft = 0,
Unpublished = 1,
Published = 2
}
// 默认情况下,值是从0开始的自增长,所以以上的声明可以不写值
enum PostStatus {
Draft, Unpublished, Published
}
// 枚举的值也可以是字符串,但这时候就需要明确的初始化了。
enum PostStatus {
Draft = 'draft', Unpublished = 'unpublished', Published = 'published'
}
const post = {
// 使用.符号来使用枚举类型的值
status: PostStatus.Draft
}
如果不需要通过索引来找到值或者键,则建议使用常量枚举类型,在enum之前加入const。这样枚举类型代码则不会入侵到源代码中,生成的代码只会使用枚举的值。
const enum PostStatus {
Draft, Unpublished, Published
}
// b为可选参数
function func1(a: number, b?: number, ...rest: number[]): string {
return 'func1';
}
const func2: (a: number, b?number) => string = function(a: number, b?: number): string {
return 'func2';
}
使用any来注解类型,同样也是弱类型。
type Uni = string | number; // 定义类型别名Uni,string和number构成的联合类型
type Uni = string & number; // 定义类型别名Uni, string和number构成的联合类型
根据代码中变量的使用情况来对变量的类型做推断。经过类型推断的变量如果再重新赋予其他类型的值,就会报错。
建议为变量明确类型,而不是使用隐式推断。
来断言(在JSX语法下这种方式会产生混淆,以为<>)const nums = [100, 101, 102];
const res = nums.find(i => i > 1); // 如果不使用断言,这里TypeScript会推断res类型为number | undefined,因为TypeScript并不会执行代码。实际上,这里的res一定是number。
const square = res * res; // 如果不进行类型断言,这里的res会报错,因为undefined类型不可以这样运算
const num1 = res as number; // as + 类型,断言
const num2 = <number>res; // 断言
interface Post {
title: string;
content: string;
subtitle?: string; // 可选成员
readonly summary: string;
}
// 缓存对象中动态成员
interface cache {
// 使用[]来表示未命名的成员以及成员名的类型
[prop: string]: string
}
function printPost(post: Post) {
console.log(post.title, post.content);
}
描述一类具体对象的抽象成员
TypeScript增强了class的相关语法
class Person {
// 需要先对成员进行类型注解
name: string, // 可以在类成员定义时初始化,也可以在构造函数中初始化
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHi(msg: string): void {
console.log(`I am ${this.name}, ${msg}`);
}
}
class Person {
// 访问修饰符对成员的访问控制
public name: string, // 可以在类成员定义时初始化,也可以在构造函数中初始化
private age: number,
protected gender: boolean
constructor(name: string, age: number) {
this.name = name;
this.age = age;
this.gender = true;
}
sayHi(msg: string): void {
console.log(`I am ${this.name}, ${msg}`);
}
}
如果构造函数使用private修饰之后,则外部无法调用构造函数来创建实例。除非类暴露出静态方法来供外部调用。
使用readonly关键字,跟在访问修饰符的后面,在成员名之前。
接口是一种抽象的规范,类可以是抽象的规范,也可以是具体的实现。类可以使用implements关键字来表示对接口的实现。
// 定义一个接口,规范了该接口必须要有的成员,但成员是抽象的,不必具体实现
interface EatAndRun {
eat(food: string): void;
run(distance: number): void;
}
// 定义了一个Person类,该类具体实现了接口EatAndRun的规范
class Person implements EatAndRun {
eat(food: string): void {
console.log(`优雅地进餐: ${food}`);
}
run(distance: number): void {
console.log(`直立行走: ${distance}`);
}
}
// 定义了一个Animal类,该类也具体实现了接口EatAndRun的规范
class Animal implements EatAndRun {
eat(food: string): void {
console.log(`呼噜呼噜地吃: ${food}`);
}
run(distance: number): void {
console.log(`爬行: ${distance}`);
}
}
更合理的是,一个接口应该只规范一个成员
// 定义Eat接口
interface Eat {
eat(food: string): void;
}
// 定义Run接口
interface Run {
run(distance: number): void;
}
// 定义了一个Person类,该类具体实现了接口Eat、Run的规范
class Person implements Eat, Run {
eat(food: string): void {
console.log(`优雅地进餐: ${food}`);
}
run(distance: number): void {
console.log(`直立行走: ${distance}`);
}
}
// 定义了一个Animal类,该类也具体实现了接口Eat、Run的规范
class Animal implements Eat, Run {
eat(food: string): void {
console.log(`呼噜呼噜地吃: ${food}`);
}
run(distance: number): void {
console.log(`爬行: ${distance}`);
}
}
// 创建一个长度为length、元素为value的数组
function createNumberArray(length: number, value: number): number[] {
// const arr = Array(length).fill(value); 这种方式下使用Array(length)得到的数组元素是any类型,在Array标准库声明文件中,使用的是Array泛型,指的是在使用Array()方法时,再指定中T的类型。
const arr = Array<number>(length).fill(value);
return arr;
}
可以在声明时使用泛型参数,等到调用时再指定泛型参数的类型。这样可以更大程度复用代码。
// 创建一个长度为length、元素为value的数组,这里数组的元素是任意类型的,只有调用时传入具体类型。
function createArray<T>(length: number, value: <T>): <T>[] {
// const arr = Array(length).fill(value); 这种方式下使用Array(length)得到的数组元素是any类型,在Array标准库声明文件中,使用的是Array泛型,指的是在使用Array()方法时,再指定中T的类型。
const arr = Array<T>(length).fill(value);
return arr;
}
// 调用时指定泛型参数为string
const arr = createArray<string>(3, 'hello');
npm install @types/模块名 -D
。一般类型声明文件是.d.ts后缀名文件,命名以@types/开头。