类型系统
强类型 与 弱类型(类型安全角度)
- 强类型:在语言层面限制函数的实参类型必须与形参类型相同
- 弱类型语言不会限制实参的类型
- 强类型有更强的类型约束,不允许有任意的隐式类型转换
- 弱类型中几乎没有什么约束,允许任意的数据隐式类型转换
- 变量类型允许随时改变的特点不是强弱类型的差异
静态类型 与 动态类型(类型检查角度)
- 静态类型:
- 一个变量声明时它的类型就是明确的
- 声明过后,它的类型就不允许再修改
- 动态类型:
- 运行阶段才能明确变量类型,而且变量类型可以随时发生变化
- 变量是没有类型的,变量中存放的值是有类型的
JavaScript 类型系统特征
- 弱类型且动态
- 缺失类型系统可靠性
- 原因:
- 早前 JavaScript 应用简单
- JavaScript 没有编译环节,设置成静态类型语言没有意义
弱类型产生的问题
- 只有运行阶段才能发现是否有问题
- 类型不确定造成的函数功能问题,结果和预想的不一样( + )
- 存在许多的隐式转换
- 对于小项目可以通过约定来约束,但是开发周期长等的项目会有很大的隐患
强类型的优势
- 错误更早暴露
- 代码更智能,编码更准确
- 重构更牢靠
- 减少不必要的类型判断
概述
- TypeScript 是 JavaScript 的超集
- TS 使用更加完善的类型系统和新特性完成开发工作,完成开发工作后会编译成能够在生产环境运行的 JavaScript 代码
- 任何一种 JavaScript 运行环境都支持
- 功能比 flow 更为强大,生态更加健全、更完善
- 属于 渐进式
- 缺点:
- 语言本身多了很多概念
- 项目初期,会增加一些成本
基础使用
- 安装:yarn init --yes、yarn add typescript --dev
- node_modules/bin/tsc:tsc 命令帮助我们编译 TS 代码(yarn tsc 文件)
- 配置:yarn tsc --init 初始化配置文件
- target:设置编译后的 JavaScript ECMAScript 标准
- module:输出的代码会采用什么样的方式去模块化
- outDir:编译结果输出的文件夹 dist
- rootDir:源代码所在的文件夹 src
- sourceMap:开启源代码映射
- strict:严格模式
- lib:指定所引用的标准库
- 只有直接编译整个项目才会运行配置文件
- 中文错误消息:yarn tsc --locale zh-CN
类型
原始类型
- 非严格模式都可以为空
- 标准库:内置对象所对应的声明
// 原始数据类型
// 严格模式不能为空 null
const a: string = "";
const b: number = 0;
const c: boolean = true;
const d: null = null;
const e: undefined = undefined;
// void 只能存放 null/undefined
const f: void = undefined;
// target 改为2015+ 或者改 lib 引用库
const g: symbol = Symbol()
Object 类型
- 泛指所有的非原始类型,也就是数组、函数、对象等
- 如果要具体的对象类型 用字面量的形式
- 赋值的对象结构必须与定义的结构完全一致
数组类型
- 可以使用数组泛型
- 元素类型[]
// 数组泛型
const ary1:Array<number> = [1,2]
// 元素类型[]
const ary2:number[] = [1,2]
元组类型
- 明确元素数量
- 明确元素类型
- 每个元素不必完全相同
- 特殊的数组,可以使用下标访问
枚举类型
- 可以给一组数值取别名
- 只会存在一些固定的值,不会出现超出范围的可能性
- 会入侵到运行时的代码,会生成键值对保存起来,让我们更好的根据值获取名称
- 如果我们确定我们不会采用索引器的方式去访问名称,那么可以采用常量枚举
// 如果不指定值会依次累加
// 数值枚举:若指定第一个值 会在第一个值的基础之上进行累加
// 字符串枚举:只能自己手动赋值
// 添加 const 关键字 变成常量枚举
const enum Status {
ok = 0,
eror = 1,
}
函数类型
- 函数定义方式:函数声明和函数表达式
- 函数声明:
- 参数的注解跟在其后 返回值的注解跟在括号后
- 参数后面添加 ? 变成可选参数
- 参数后面添加默认值之后 也会变成可选参数
- 可选参数必须在必选参数后面
- 函数表达式
- 可以采用箭头函数的形式
function fun1(a:number = 10,b?:number,res:number[]):string {
return 'fun1'
}
// 函数表达式
const fun2: (a?: number, b?: number | undefined) => string = function(a:number = 10,b?:number):string{
return 'fun2'
}
const fun3 = function(a:number = 10,b?:number):string{
return 'fun3'
}
任意类型(弱类型)
- 动态类型
- 不安全,轻易不使用
隐式类型推断
- 在没有进行类型约束直接赋值时,会根据赋值类型来推断该类型
- 推断出来后就相当于添加了 :类型
- 没有推断出来就会是 any
类型断言
- 当我们明确知道类型但是 TypeScript 不一定知道,此时我们可以明确告诉 TypeScript 类型
- 方式:
<类型>(JSX 下不能使用)、as
- 类型断言并不是类型转换(运行时的概念),只是编译过程中的概念,编译过后就不会存在了
const nums = [100,200,300]
const res = nums.find( x => x>0)
let age1 = res as number
let age2 = <number>res // JSX下不能使用
作用域问题
- 直接在文件中定义的变量是在全局作用域中的
- 我们可以使用立即执行函数进行包裹或者采用模块导出(就会成为模块作用域中的局部成员)的方式
(function() {const a = 123})()
const a = 123
export {}
接口
- 一种规范或者契约,抽象概念
- 约定对象的结构、约定有哪些成员而且这些成员的类型又是什么样的
- 使用整个接口就必须遵循接口全部的约定
- 使用
interface
关键字 -
?
:可选成员 -
readonly
修饰成员:只读成员,可以跟在访问修饰符后面 -
[类型]:类型
:动态成员
interface Student {
name: string;
readonly age: number;
position?:string;
}
function banji(student: Student) {
console.log(student);
}
// 动态成员 不定数量 不定名称
interface Eat {
[key:string] : string
}
类
- 描述一类事物的抽象特征
- 用来描述一类具体对象的抽象成员
- ES6 之前 :函数 + 原型 模拟实现类
- ES6+:专门的 class
- TS 增强了 class 的相关语法
class Person {
// 必须声明和初始化
name:string //= 'zhangsan' 初始化一般在构造函数中进行
age:number
constructor(name:string,age:number) {
this.name = name
this.age = age
}
}
访问修饰符
- private:私有属性,只能在类中访问
- public:公有成员,默认
- protected:也不能在外部访问,只允许在子类中访问,允许继承
- 如果给了构造函数添加了 private ,那么该类就不能被实例化也不能被继承,就只能在内部添加 静态方法,在静态方法中创建这个类的实例
类与接口
- 接口可以对类进行抽象
- 类后跟
implements
关键去实现接口 - 实现一个接口后就必须包含接口中的全部方法和成员
interface EatFood {
eat (food: string): void;
}
class Animal implements EatFood{
eat(food:string) {
console.log("我吃:",food);
}
}
抽象类
- 包含子类当中必须要有的成员,不同于的接口的是可以包含一些具体的实现
- 比较大的类建议使用抽象类(例如:动物)
- 只能被继承不能使用 new 的方式创建
- 类前面使用
abstract
关键字
// 抽象出类中的公共的一些方法和属性
interface EatFood {
eat (food: string): void;
}
// ----------抽象类--------
abstract class Animal implements EatFood{
eat(food:string) {
console.log("我吃:",food);
}
// 抽象方法 没有方法体
abstract run(speed:number):void
}
class Cat extends Animal{
// 继承了抽象类就必须实现其方法
run(speed: number): void {
console.log('跑的速度:',speed);
}
}
const cat = new Cat()
cat.run(100)
泛型
泛型:指在定义函数、接口、类的时候,不去指定具体的类型,等到在使用的时候再去指定具体的类型
- 为了极大的复用代码
- 在定义的时候不明确的类型都采用
T
来代表 - 在调用的时候把类型传递过去
function creatArray<T>(length:number,value:T):T[] {
return Array<T>(length).fill(value)
}
console.log(creatArray<number>(3,100));
类型声明
- 一个成员在定义的时候因为种种原因没有进行声明一个明确的类型
- 我们就可以在使用的时候单独为它进行类型声明
- 使用
declare
关键字进行声明 - 由于现在的社区非常完善,我们也可以安装相应的类型声明模块就行了