语言类型
按照类型安全区分
强类型语言和弱类型语言
按照类型检查区分
静态类型语言和动态类型语言
强类型和弱类型
强类型语法上不允许任意的隐式类型转换,弱类型允许
弱类型语言的问题:
- 类型异常需要等到程序运行才会被发现
const obj = {};
obj.fn();
- 函数功能改变
const sum = (a, b) => a + b;
sum(1, '1');
- 对象的键
const obj = {};
obj[true] = 1; // 键实际上是'true'
强类型语言的优势:
- 错误更早暴露
- 代码更智能,编码更准确(编辑器会时时刻刻提示类型)
- 重构更牢靠
- 减少不必要的类型判断(不再需要用typeof判断)
静态类型和动态类型
静态类型
变量声明时类型是明确的,声明之后不能再修改
动态类型
运行阶段才能够明确变量类型,且可以随时改变
Flow
js的类型检查器
使用步骤:
- 命令行:yarn add flow-bin 安装
- 第一行写 //@flow
- 关掉vscode设置里的JavaScript › Validate: Enable 禁用js验证
- 命令行:yarn flow init 初始化.flowconfig文件
- 命令行:yarn flow 执行命令
- 命令行:yarn flow stop 结束服务
编码结束后移除类型注解的两种方法:
- flow-remove-types 安装 yarn add flow-remove-types 运行 yarn flow-remove-types . -d dist (.表示本文件,可以写src)
- babel/preset-flow
开发工具插件: vscode的flow language support 保存代码后才会检测出类型错误
类型注解
- 函数参数 function sum (a: number, b: number) { return a + b }
- 变量 let num: number = 100
- 函数返回值 function fn (): void {} // ovid表示无返回值
各种类型的写法
-
基本类型: string、number(包括NaN和Infinity)、boolean、null(值只能是null)、void(表示undefiened)、symbol (用 | 隔开表示或)
-
数组: Array、number[](两种均全部由数字组成的数组)
[string, number](长度为2,第一项为字符串,第二项为数字的数组)(明确元素数量及所有元素类型的数组叫元组)
- 对象: {foo: string, bar: number}(有值为字符串的foo属性和值为数字的bar属性的对象)
{foo: string, bar?: number}(同上,但是bar可选)
{[string]: string}(运行添加任意个数的属性,但是属性和值必须都是字符串)
-
函数: (string, number) => void(两个参数,第一个是字符串,第二个是数字,且没有返回值的函数)
-
特殊类型: 'foo'(只能为'foo') 'success' | 'warning' | 'danger'(只能为三个值之一)
-
type关键词声明类型 type stringOrNumber = string | number stringOrNumber可以作类型用
-
maybe类型
-
mixed(任一类型,但仍然是强类型,如果有使用隐患的话就会报错,要用typeof进行类型判断) any(任一类型,弱类型)
flow.org/en/docs/typ… www.saltycrane.com/cheat-sheet…
- 浏览器api 获取页面元素时的类型:HTMLElement | null
TypeScript
Javascript的超集(superset)
优点:
- 任何一种js运行环境都支持
- 功能比flow强大,生态更完善
缺点:
- 语言本身多了很多概念
- 项目初期,代码编写的成本增加
命令:
tsc(TypeScript compile)
yarn tsc '文件路径' 可以把特定的ts文件编译为js文件
yarn tsc 不写路径,则编译tsconfig.json里rootDir目录下的所有ts文件
yarn tsc --locale zh-CN 打印的中文的错误
在vscode的设置中查typescript locale可以设置中文错误提示
yarn tsc --init 命令生成ts的配置文件tsconfig.json
tsconfig.json选项:
target 编译后的js文件采用的es标准,默认es5
module 输出后的代码选择的模块化,默认commonjs
lib 额外的标准库文档,可添加,默认[](一旦添加了新的标准库,会覆盖原有的,要加回"DOM")
标准库就是内置对象所对应的声明
sourceMap 是否开启源代码映射,默认true(scourceMap文件调试源代码?)
outDir 编译结果输出到的文件夹,默认"./"
rootDir 源代码的目录,默认"./"
strict 是否开启严格模式,默认true(函数参数必须有类型,不能是any)
strictNullChecks 是否允许已设置类型的变量,设置为null
。。。。。。
类型注解(大多数与flow相同)
-
非严格模式下,已设定类型的变量,可以为null (可以单独设置strictNullChecks)
-
ts的标准库是es5,因此编译es6新增的symbol会报错,任何es6新增的对象都会报错 解决:把配置文件里的target改为es2015,或者在lib里加上"es2015"
-
object类型可以泛指所有的复杂数据类型,对象字面量的写法单指对象
-
冒号前加?,意为可选成员
-
不同文件的全局作用域有同名的变量会报错,需要分别放入单独的作用域中
枚举类型
enum关键字
enum Status {
pending = 1,
success = 2,
fail = 3
}
// 值都是连续数字的话,可以缩写为
enum Status {
pending = 1, // 值是从0开始的话可以。第一个也可以不写值
success, // 依次累加
fail
}
// 使用
const obj = {
status: Status.pending
}
// 可以用索引的方法获取枚举类型的值
Status[0]
// 如果不需要用索引的方法获取枚举类型的值,最好将枚举类型设置为常量
const enum Status {
pending = 1,
success = 2,
fail = 3
}
接口
约定对象有那些成员和各自的类型,interface关键字
interface Person {
name: string; // 用分号隔开
age: number;
readonly gender: boolean; // readonly关键字 只读,初始化后就不能修改
nickname?: string;
}
const bob: Person = {
name: 'bob',
age: 18,
gender: true
}
interface Standard {
[prop: string]: string // prop词可以用其他词替换
}
// 对象可以有任意属性,但是键值必须都是字符串
类里的写法
class Person {
name: string // 类的属性使用前必须声明类型
age: number
constructor (name: string, age: number) {
this.name = name;
this.age = age;
}
}
类的访问修饰符
class Person {
public name: string
private age: number
protected gender: boolean
constructor (name: string, age: number) {
this.name = name;
this.age = age;
}
sayAge () {
console.log(this.age); // 私有属性只能在内部使用
}
}
class Child extends Person {
constructor (name: string, age: number) {
super(name, number);
console.log(this.gender); // 受保护属性只能在子类内部使用
}
}
public关键字
表示公有成员,不写也一样,推荐写
private关键字
表示私有成员,只能在内部访问
protected关键字
表示受保护的成员,只能在类内部和子类内部访问
类的只读属性
关键字readonly,写在访问修饰符后面
只能在初始化或者构造函数里赋值,之后就不能再改
类的构造函数加private关键字,则类不能在外部生成实例,可以用静态方法在类的内部创建实例
class Person {
public name: string
private age: number
private constructor (name: string, age: number) {
this.name = name;
this.age = age
}
static create (name: string, age: number) {
return new Person(name, age);
}
}
const bob = Person.create('bob', 20);
类与接口
约束一个类 interface和inplements关键字
interface run {
run (distance: string): void
}
interface eatAndDrink { // 一个接口最好只约束一个属性
eat (food: string): void
drink (wine: string): void
}
class Person implements run, eatAndDrink { // implements后面可以跟多个接口
run (distance: string): void {
console.log(`run ${distance}`);
}
eat (food: string): void {
console.log(`eat ${food}`);
}
drink (wine: string): void {
console.log(`eat ${wine}`);
}
}
抽象类
与接口的区别,抽象类里的方法有方法体 abstract关键字
abstract class Animal {
eat (food: string) {
console.log(`eat ${food}`);
}
abstract run (distance: number): void
// 抽象类里的抽象方法不用写方法体
}
class Dog extends Animal { // 继承了抽象类里的方法eat
// 父类里有抽象方法,字类必须实现
// 可以用vscode的代码修正功能:点击字类名称,ctrl + .
run(distance: number): void {
console.log(`run ${distance}`);
}
}
泛型
function creatNumberArray (length: number, value: number): number[] {
return Array<number>(length).fill(value);
}
function creatStringArray (length: number, value: string): string[] {
return Array<string>(length).fill(value);
}
// 用泛型改进,T是泛型参数
function createArray<T> (length: number, value: T): T[] {
return Array<T>(length).fill(value);
}
类型声明
在ts文件里引入js库,如果该js库自带的类型声明模块,就需要根据vscode提示下载类型声明模块 否则需要手写类型声明 declare关键字
declare function fn (params: number): string;