文章目录
- 概述
- 类型系统
- 1、强类型与弱类型
- 2、静态类型与动态类型
- 3、JavaScript 类型系统特征
- 4、弱类型的问题
- 5、强类型的优势
- 6、Flow
- 三、TypeScript
- 1、 TypeScript 概述
- 2、 TypeScript快速上手
- 3、 TypeScript 配置文件
- 4、 TypeScript原始类型
- 5、 TypeScript 标准库
- 6、TypeScript 中文错误消息
- 7、 TypeScript 作用域问题
- 8、 TypeScript Object 类型
- 9、 TypeScript 数组类型
- 10、 TypeScript 元组类型 Tuple Type
- 11、 TypeScript 枚举类型 Enum Types
- 12、 TypeScript 函数类型 Function Types
- 13、 TypeScript 任意类型 Any Types
- 14、 TypeScript 隐式类型推断 Type Inference
- 15、 TypeScript 类型断言 Type assertions
- 16、 TypeScript 接口 Interfaces
- 17、 TypeScript 接口补充
- 18、 TypeScript 类的基本使用 Classes
- 19、 TypeScript 类的访问修饰符
- 20、 TypeScript类的只读属性
- 21、 TypeScript 类与接口
- 22、 TypeScript 抽象类
- 23、 TypeScript 泛型 Generics
- 24、 TypeScript 类型声明
概述
强类型与弱类型
静态类型与动态类型
JavaScript自有类型系统的问题
Flow静态类型检查方案
TypeScript语言规范与基本应用
类型系统
- 强类型与弱类型(类型安全)
- 静态类型与动态类型(类型检查)
1、强类型与弱类型
类型安全:强类型 vs. 弱类型
强类型定义:语言层面限制函数的实参类型必须与形参类型相同
弱类型定义:语言层面不会限制实参的类型
普遍认为:强类型有更强的类型约束,而弱类型中几乎没有什么约束
强类型语言中不允许任意的隐式类型转换,而弱类型语言则允许任意的数据隐式类型转换
2、静态类型与动态类型
类型检查:静态类型语言 vs. 动态类型语言
静态类型语言:一个变量声明时它的类型就是明确的,而变量声明过后,它的类型就不允许再修改
动态类型语言:在运行阶段才能明确变量类型,而且变量的类型随时可以改变,也可以说动态类型语言中的变量没有类型,而变量中存放的值是有类型的
3、JavaScript 类型系统特征
JavaScript类型系统特征:弱类型 且 动态类型 (任性:缺点:缺失了类型系统的可靠性)
JavaScript是一门脚本语言,脚本语言的特点是没有编译环节(JavaScript 没有编译环节)
4、弱类型的问题
JavaScript 的弱类型语言当中要等到运行阶段才发现代码类型的一些异常,如果放在定时器则要等到一段时间才发现一次则会造成没有发现代码类型异常
const obj = {}
obj.foo() // TypeError: obj.foo is not a function
setTimeout(() => {
obj.foo()
}, 100000)
因为类型不确定引起这种问题
function sum (a, b) {
return a +b
}
console.log(sum(100, 100)) //200
console.log(sum(100, '100')) // 100100
const obj = {}
obj[true] = 100
console.log(obj['true']) // 100
5、强类型的优势
- 编码阶段错误更早暴露
- 代码更智能,编码更准确
- 重构更牢靠
6、Flow
Flow: JavaScript的类型检查器,在代码中添加一些类型注解的方式来去标记代码中每个变量或者参数是什么类型的,Flow 根据类型注解检查代码中的一些类型异常,从而实现开发阶段对类型的一些检查,这就避免了在编译的时候才发现这些类型使用的错误
Flow 不要求对每个参数都做类型注解,可以在只需要类型注解的地方加上类型注解
Flow 只是一个小工具
6.1、Flow 快速上手
1、先安装一个Flow检查工具 flow-bin
2、然后在使用的文件添加 @flow 标记
三、TypeScript
1、 TypeScript 概述
TypeScript是JavaScript的超集(superset)
所谓 超集是在JavaScript原有特性上多了一些扩展特性
多出来的是一套强大的类型系统和ES6+新特性的支持,最终会被编译成原始的JavaScript
优势:
- 避免在开发过程中的有可能会出现的类型异常提高编码的效率及代码的可靠程度
- 任何一种JavaScript运行环境都支持
- 功能更为强大,生态也更健全、更完善
缺点:
- 语言本身多了很多概念(提高学习成本)(好在typescript 属于渐进式的)
- 项目初期,TypeScript会增加一些成本
2、 TypeScript快速上手
使用TypeScript的一個基本過程
- 1)、安裝一個TypeScript的模塊,可以安裝在全局也可以安裝在本地
- 2)、提供了一個 tsc 的命令,實際上是 TypeScript complier
- 3)、通過tsc編譯一個文件,編譯成.js文件,檢查代碼類型異常
3、 TypeScript 配置文件
4、 TypeScript原始类型
5、 TypeScript 标准库
标准库就是内置对象所对应的声明
6、TypeScript 中文错误消息
7、 TypeScript 作用域问题
解决这个问题的方法
// 1、用一个立即执行函数创建一个单独作用域
// (function () {
// const a = 123
// })()
// 2、 加上 export {}
const a = 123
export {}
8、 TypeScript Object 类型
Object 类型不单指对象,指的是除了原始对象外的其他类型
export {}
const foo: object = function () {} // [] // {}
// 对象类型可以使用这种字面量的方式,更专业的方式是用接口
const obj: {foo: number, bar: string} = {foo: 123, bar: 'string'}
9、 TypeScript 数组类型
export {}
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)
}
10、 TypeScript 元组类型 Tuple Type
元组类型是一种特殊的数据结构,元组就是明确元素数量和元素类型
这种元组一般可以 用来在一个函数当中去返回多个返回值,这种类型在现在越来越常见了
// 元组 (Tuple)
export {}
const tuple: [number, string] = [18, 'zce']
// const age = tuple[0]
// const name = tuple[1]
// 数组解构方式提取每个元素
const [age, name] = tuple
// 这种元组一般可以 用来在一个函数当中去返回多个返回值,这种类型在现在越来越常见了
// 例如 ECMAScript2017
Object.entries({
foo: 123,
bar: 456
})
11、 TypeScript 枚举类型 Enum Types
1、 也可以不指定默认值,不指定的话默认从 0 开始累加;如果给第一个成员指定值后面的成员会根据第一个累加
2、 枚举除了是数字类型的,也可以是字符串类型的,字符串类型的要指定默认值,实际字符串类型的不常见
注意:枚举会入侵到我们运行时的代码,(它会影响到我们编译后的结果,枚举编译后会编译成 双向的键值对对象)
export {} // 确保跟其它示例没有成员冲突
// // 1、 也可以不指定默认值,不指定的话默认从 0 开始累加;如果给第一个成员指定值后面的成员会根据第一个累加
enum PostStatus {
Draft,
Unpublished,
Published
}
const post = {
title: 'Hello TypeScript',
content: 'TypeScript is a type superset of JavaScript.',
status: PostStatus.Draft // 2 // 1 // 0
}
编译后:
"use strict";
// 枚举类型
Object.defineProperty(exports, "__esModule", { value: true });
// // 1、 也可以不指定默认值,不指定的话默认从 0 开始累加;如果给第一个成员指定值后面的成员会根据第一个累加
var PostStatus;
(function (PostStatus) {
PostStatus[PostStatus["Draft"] = 0] = "Draft";
PostStatus[PostStatus["Unpublished"] = 1] = "Unpublished";
PostStatus[PostStatus["Published"] = 2] = "Published";
})(PostStatus || (PostStatus = {}));
var post = {
title: 'Hello TypeScript',
content: 'TypeScript is a type superset of JavaScript.',
status: PostStatus.Draft // 2 // 1 // 0
};
3、 如果确认不会使用索引器的方式访问枚举,那我们就建议用 常量枚举
// 3、 如果确认不会使用索引器的方式访问枚举,那我们就建议用 常量枚举
const enum PostStatus {
Draft,
Unpublished,
Published
}
const post = {
title: 'Hello TypeScript',
content: 'TypeScript is a type superset of JavaScript.',
status: PostStatus.Draft // 2 // 1 // 0
}
编译后:
// 枚举类型
Object.defineProperty(exports, "__esModule", { value: true });
var post = {
title: 'Hello TypeScript',
content: 'TypeScript is a type superset of JavaScript.',
status: 0 /* Draft */ // 2 // 1 // 0
};
12、 TypeScript 函数类型 Function Types
1、 可选参数 可以在参数后加 ? ,也可以给默认值,不过都要在参数列表的最后面
// function func1 (a: number, b?: number) : string {
// 或者 默认值
function func1 (a: number, b: number = 10) : string {
return 'func1'
}
2、 如果想任意个参数,可以用 rest 参数
function func1 (a: number, b: number = 10, ...rest: number[]) : string {
return 'func1'
}
func1(100, 200)
// func1(100)
// func1(100, 200, 300)
函数表达式
也可以用箭头函数的方式
// func2: function (a: number, b: number) => string
const func2 = function (a: number, b: number) : string {
return 'func2'
}
13、 TypeScript 任意类型 Any Types
// 任意类型 (弱类型)
export {} // 确保跟其它示例没有成员冲突
function stringify (value: any) {
return JSON.stringify(value)
}
stringify('string')
stringify(100)
stringify(true)
let foo: any = 'string'
foo = 100
// foo.bar()
// any 类型是不安全的
14、 TypeScript 隐式类型推断 Type Inference
虽然 TypeScript 支持隐式类型推断,但是建议为每个变量添加明确的类型
// 隐式类型推断
export {} // 确保跟其它示例没有成员冲突
// 虽然 TypeScript 支持隐式类型推断,但是建议为每个变量添加明确的类型
let age = 18
// age = 'string'
let foo
foo = 100
foo = 'string'
15、 TypeScript 类型断言 Type assertions
类型断言不是类型转换,这里不是把一个类型转换成另一个类型,因为类型转换是代码运行时的一个概念,而类型断言只是编译过程的一个概念,编译过后这个概念也就不存在了
// 类型断言
export {} // 确保跟其它示例没有成员冲突
// 假定这个 nums 来自一个明确的接口
const nums = [110, 120, 119, 112]
const res = nums.find(i => i > 0)
// const square = res * res
const num1 = res as number
const num2 = <number>res // JSX 下不能使用,建议用第一种
16、 TypeScript 接口 Interfaces
接口就是用来约束对象的结构,一个对象去实现一个接口,它就必须要与拥有这个接口当中所拥有的所有成员
// 接口
export {} // 确保跟其它示例没有成员冲突
// 定义接口
interface Post {
title: string
content: string
}
function printPost (post: Post) {
console.log(post.title)
console.log(post.content)
}
printPost({
title: 'Hello TypeScript',
content: 'A javascript superset'
})
17、 TypeScript 接口补充
接口: 可选成员、只读成员
export {} // 确保跟其它示例没有成员冲突
// =========================
// 定义接口
interface Post {
title: string
content: string
subtitle?: string // 可选成员
readonly summary: string // 只读成员
}
const hello: Post = {
title: 'Hello TypeScript',
content: 'A javascript superset',
summary: 'A javascript'
}
// hello.summary = 'other' //不可以再修改
动态成员
interface Cache {
[key: string]: string
}
const cache: Cache = {}
cache.foo = 'value1'
cache.bar = 'value2'
18、 TypeScript 类的基本使用 Classes
描述一类具体事物的抽象特征
用来描述一类具体对象的抽象成员
ES6以前,都是通过 函数 + 原型 模拟实现类
ES6开始 JavaScript中有了专门的class
TypeScript增强了class的相关语法
// 类 (Class)
export {} // 确保跟其它示例没有成员冲突
class Person {
name: string // = 'init name'
age: number
construtor (name: string, age: number) {
this.name = name
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
}
}
19、 TypeScript 类的访问修饰符
1、public, private,protected 控制类的可访问级别
2、protected 只允许在子类当中访问对应的成员
3、 构造函数constructor 默认也是 public, 前面也可以加public, private,protected,加了private,这个类型在外部不能实例化和访问了,protected也是不允许实例化的,不过是可以继承的
// 类 (Class)
export {} // 确保跟其它示例没有成员冲突
// public, private,protected 控制类的可访问级别
class Person {
public name: string // = 'init name' // 默认是 public 加不加都无所谓,不过还是建议加上,因为更加容易理解一点
private age: number
protected gender: boolean // 受保护的
construtor (name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
console.log(this.age)
}
}
// constructor 默认也是 public, 前面也可以加public, private,protected,加了private,这个类型在外部不能实例化和访问了,protected也是不允许实例化的,不过是可以继承的
// 这个时候可以访问到 gender,protected 只允许在子类当中访问对应的成员
class Student extends Person {
private construtor (name: string, age: number) {
super(name, age)
console.log(this.gender) // 可以访问到
}
static create (name: string, age: number) {
return new Student(name, age)
}
}
const tom = new Person('tom', 18)
console.log(tom.name)
// console.log(tom.age) // 无法访问,因为这个时候 age是私有属性
// console.log(tom.gender) // 无法访问
const jack = Student.create('jack', 18)
20、 TypeScript类的只读属性
设置了 readonly 不管内部还是外部都不允许修改了
// 类的只读属性
export {} // 确保跟其它示例没有成员冲突
class Person {
public name: string // = 'init name' // 默认是 public 加不加都无所谓,不过还是建议加上,因为更加容易理解一点
private age: number
protected readonly gender: boolean // 受保护的,设置了 readonly 不管内部还是外部都不允许修改了
construtor (name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi (msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
console.log(this.age)
}
}
const tom = new Person('tom', 18)
console.log(tom.name)
// tom.gender = false // 不能修改
21、 TypeScript 类与接口
// 类与接口
export {} // 确保跟其它示例没有成员冲突
// interface EatAndRun {
// eat (food: string): void
// run (run: string): void
// }
// 两件事情最好分两个接口
interface Eat {
eat (food: string): void
}
interface Run {
eat (food: string): void
}
class Person implements Eat, Run {
eat (food: string): void {
console.log(`优雅的进餐: ${food}`)
}
run (distance: number) {
console.log('直立行走: ${distance}')
}
}
class Animal implements Eat, Run{
eat (food: string): void {
console.log(`呼噜呼噜的吃: ${food}`)
}
run (distance: number) {
console.log('爬行: ${distance}')
}
}
// class Person {
// eat (food: string): void {
// console.log(`优雅的进餐: ${food}`)
// }
// run (distance: number) {
// console.log('直立行走: ${distance}')
// }
// }
// class Animal {
// eat (food: string): void {
// console.log(`呼噜呼噜的吃: ${food}`)
// }
// run (distance: number) {
// console.log('爬行: ${distance}')
// }
// }
22、 TypeScript 抽象类
约束子类当中必须要有一个成员,抽象类可以包含具体的实现,接口只可以是成员的一些抽象,不包含具体的实现,一般大的类目都用抽象类
// 抽象类
export {} // 确保跟其它示例没有成员冲突
class Animal {
eat (food: string): void {
console.log(`呼噜呼噜的吃: ${food}`)
}
abstract run (distance: number): void
}
class Dog extends Animal {
run(distance: number): void {
console.log('四脚爬行', distance)
}
}
const d = new Dog()
d.eat('嗯西马')
d.run(100)
23、 TypeScript 泛型 Generics
泛型就是把定义的时候不能明确的类型变成一个参数,使用的时候再传递类型参数
// 泛型
export {} // 确保跟其它示例没有成员冲突
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<number>(length).fill(value)
return arr
}
function createArray<T> (length: T, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
// const res =createNumberArray(3, 100)
// // res => [100, 100, 100]
const res = createArray<string>(3, 'foo')
24、 TypeScript 类型声明
引用第三方声明模块
一般: @type/lodash 类型声明模块
// 类型声明
import { camelCase } from 'lodash'
import qs from 'query-string'
qs.parse('?key=value&key2=value2')
// declare function camelCase (input: string): string
const res = camelCase('hello typed')