#### TypeScript 基于JavaScript之上的语言 解决了JavaScript类型系统的问题
- 强类型与弱类型
- 静态类型与动态类型
- Javascript自由类型系统的问题
- Flow静态类型检查方案
- TypeScript语言规范与基本应用
#### 类型系统
- 强类型与弱类型(类型安全) 强类型有更强的类型约束,而弱类型中几乎没有什么约束
- 强类型语言中不允许任意的隐式类型转换
- 弱类型语言则允许任意的数据隐式类型转换
- 静态类型与动态类型(类型检查)
- 静态类型: 一个变量声明时他的类型就是明确的, 声明过后, 它的类型就不允许再修改
- 动态类型: 运行阶段才能够明确变量的类型且变量的类型随时可以改变(动态类型中变量没有类型, 变量中存放的值是有类型的)
- Javascript是动态类型语言
#### JavaScript类型系统特征
- 弱类型 且 动态类型 (缺失了类型系统的可靠性) JavaScript没有编译环节
#### 弱类型语言开发大型应用的常见问题
- 君子约定有隐患, 强制要求有保障
#### 强类型语言的优势
- 1. 错误更早的暴露(编译阶段就暴露)
- 2. 代码更智能, 编码更准确
- 3. 重构更牢靠
- 4. 减少不必要的类型判断
#### Flow
- JavaScript的类型检查器, 2014年Facebook推出, 弥补js弊端
- 类型注解: 加参数后边加一个冒号, 在跟上一个数据类型
- Flow只是一个小工具
#### Flow编译移除注解
- 通过编译移除[类型注解]
```javascript
// @flow
// function sum (a: number, b: number){
// return a + b
// }
// sum(100, 300)
// sum('100', '300')
// sum('100')
// ==========================
// ==========================
```
#### 开发工具插件(Flow Language Support)
- 在vscode中安装插件
#### 类型推断(Type inference)
```javascript
function square(n){
return n * n
}
square('100')
```
#### 类型注解(Type Annotations)
```javascript
function square(n: number){
return n * n
}
square('100')
let num: string
num = 100
function foo(): number {
return 10
return '10'
}
// 没有返回值的函数声明类型
function foo(): void {
}
```
#### 原始类型(Primitive Types)
```javascript
/**
* 原始类型
* @flow
*/
const a: string = 'string'
const b: number = Infinity //NaN // 100
const c: boolean = true //false
const d: null = null
const e: void = undefined
const f: symbol = Symbol()
```
#### 数组类型(Array Types)
```javascript
/**
* 数组类型
* @flow
*/
const arr1: Array<number> = [1, 2, 3, 'ee']
const arr2: number[] = [1, 2, 3]
// 元组
const foo: [string, number] = ['foo', 100]
```
#### 对象类型(Object Types)
```javascript
/**
* 对象类型
* @flow
*/
const obj1: {foo: string, bar: number} = {foo: 'string', bar: 100}
const obj2: {foo?: string, bar: number} = {bar: 100}
const obj3: {[string]: string} = {}
obj3.key1 = 'value3'
obj3.key2 = 100
```
#### 任意类型(Any Types)
```javascript
/**
* Mixed Any 任意类型
* @flow
*/
// Mixed是强类型的 Any是弱类型
// Mixed
function passMaxid (value: mixed){
if(typeof value === 'string'){
value.substr(1)
}
if(typeof value === 'number'){
value * value
}
}
passMaxid('str')
passMaxid(100)
// Any
function passAny (value: any){
value.substr(1)
value * value
}
passAny('str')
passAny(100)
```
#### 函数类型(Function Types)
```javascript
/**
* 函数类型
* @flow
*/
function foo(callback: (string, number) => void){
callback('foo string', 100)
}
foo(function(str, num){
// str => string
// num => number
})
```
#### 特殊类型
```javascript
/**
* 运行环境 API
* @flow
*/
const element: HTMLElement | null = document.getElementById('app')
const element2: HTMLElement | null = document.getElementById(100)
const element1: ?HTMLElement = document.getElementById('app')
```
#### 类型小结
- https://flow.org/en/docs/types/
- https://www.saltycrane.com/heat-sheets/flow-type/latest/
#### Flow运行环境API(内置对象)
#### TypeScript JavaScript的超集(superset)、扩展集
- 任何一种JavaScript运行环境都支持TypeScript
- 功能更为强大, 生态更健全、更完善(相比Flow)
- 前端领域的第二语言
- TypeScript属于[渐进式]
- 缺点一: 语言本身多了很多概念
- 缺点二: 项目初期, TypeScript会增加一些成本
#### 快速上手 可以完全按照 JavaScript 辨准语法编写代码
```javascript
// 可以完全按照 JavaScript 辨准语法编写代码
const hello = (name: string ) => {
console.log(`hello ${name}`)
}
hello('TypeScript')
hello(100)
```
#### TypeScript配置文件 yarn tsc --init
#### TypeScript原始类型(Primitive Types)
```javascript
const a: string = 'foo'
const b: number = 100 //NaN Infinty
const c: boolean = true //false
// const d: string = null
const e: void = undefined
const f: null = null
const g: undefined = undefined
const h: symbol = Symbol()
```
#### TypeScript标准库声明(内置对象类型)
- 标准库: 内置对象所对应的声明
#### 中文错误信息
- yarn tsc --locale zh-CN (命令行显示中文错误信息)
- vscode编译器-setting中搜索 typescript locale选项
#### TS作用域问题
```javascript
// 02-primitive-types.ts(5, 7): 此处也声明了 "a"
// const a = 223
// 第一种
// (function(){
// const a = 223
// })()
// 第二种
const a = 223
export {}
```
#### TS的Object类型(Object Types)
```javascript
const foo: object = function(){} //{} //[]
const obj: {foo: string, bar: number} = {foo: '233', bar: 222}
```
#### TS数组类型(Array Types)
```javascript
const arr1: Array<number> = [2, 3, 4]
const arr2: number[] = [3, 5,3]
//================
function sum (...args: number[]){
return args.reduce((prev, current) => prev + current, 0)
}
// sum(2, 3, 2, 'foo')
sum(2, 3, 2, )
```
#### TS元组类型(Tuple Types)
```javascript
const tuple: [number, string, number] = [1, 'dd', 22]
// const age = tuple[0]
// const name = tuple[0]
const [age, name] = tuple
// ======================
// React 中useState返回的就是元组类型数据
// Object.entries({name: 'liuchao', age: 22}) 返回的也是元组类型数据(固定长度)
```
#### TS枚举类型(Enum Types)
- 给每一个变量一个更好的名字, 见名知意
- 一个枚举中只可能出现固定的几个值, 不会出现超出范围的可能性
- **枚举类型会入侵到运行代码**
```javascript
// const PostStatus = {
// Draft: 0,
// Unpublished: 1,
// Published: 2
// }
// enum PostStatus {
// Draft = 0,
// Unpublished = 1,
// Published = 2,
// }
// 不指定默认值从0开始累加
// eq: Draft = 0, Unpublished = 1 Published = 2
// enum PostStatus {
// Draft,
// Unpublished,
// Published,
// }
// 指定初始的默认值 从初始值开始累加
// eq: Draft = 6, Unpublished = 7 Published = 8
const enum PostStatus {
Draft = 6,
Unpublished,
Published,
}
// 如果枚举类型值是字符串就无法累加, 必须指定每个枚举值
const enum PostStatusStr {
Draft = 'aaa',
Unpublished = 'ddd',
Published = 'ggg',
}
const post = {
title: 'Hello TS',
content: 'TS is a typed superset of JavaScript.',
status: PostStatus.Draft//2, //0 草稿 1 未发布 2 已发布
}
// 枚举类型会入侵到运行代码
// 编译之后会生成一个双向键值对对象
// 可以通过键获取值 也可以通过值获取键
// var PostStatus;
// (function (PostStatus) {
// PostStatus[PostStatus["Draft"] = 6] = "Draft";
// PostStatus[PostStatus["Unpublished"] = 7] = "Unpublished";
// PostStatus[PostStatus["Published"] = 8] = "Published";
// })(PostStatus || (PostStatus = {}));
// 作用: 可以通过索引获取值
// PostStatus[6] //=> Draft
// console.log(PostStatus[6])
// 如果想去掉这种双向键值对对象 在枚举声明之前加上const
// 编译之后就会消除, 枚举值会在注释中指出
// var post = {
// title: 'Hello TS',
// content: 'TS is a typed superset of JavaScript.',
// status: 6 /* Draft */ //2, //0 草稿 1 未发布 2 已发布
// };
```
#### TS函数类型(Function Types)
```javascript
function func1 (a: number, c: number = 22, b?: number): string {
return 'func1'
}
// 参数类型、个数必须完全一致
func1(100, 200)
func1(100)
func1(100, 200, 300)
// ========================
const func2:(a: number, b: number) => string = function (a: number, b: number): string {
return 'func2'
}
```
#### 任意类型(Any Types)
```javascript
function stringify (value: any){
return JSON.stringify(value)
}
stringify('2000')
stringify(222)
stringify(true)
let foo: any = 'string'
foo = 200
foo.bar()
// any类型不安全的
```
#### 隐式类型推断(Type Inference)
```javascript
let age = 111
// 不能将类型“string”分配给类型“number”。
// age = 'string'
// 如果不能推断出来变量类型则变量类型为any
let foo
foo = 22
foo = 'string'
// 建议为每个变量添加明确的类型 便于理解代码
```
#### 类型断言(Type assertions)
```javascript
// 假定这个nums来自一个明确的接口
const nums = [100, 120, 119, 112]
const res = nums.find(i => i>0)
// const square = res * res
// 断言两种方式
// 第一种 as 推荐
const num1 = res as number
// 第二种 <> 但是jsx中标签冲突 不能使用
const num2 = <number>res
const square = num1 * num1
const square2 = num2 * num2
```
#### 接口(Interface)
- 可选成员、只读成员、动态成员
```javascript
interface Post {
title: string
content: string
subtitle?: string//可选成员
readonly summary: string//只读成员
}
function printPost(post: Post): void{
console.log(post.title)
console.log(post.content)
}
printPost({
title: 'hello ts',
content: 'a js superset',
summary: 'summary'
})
const hello: Post = {
title: 'hello ts hello',
content: 'a js superset hello',
summary: 'summary hello'
}
// hello.summary = 'hello'
// -----------------------
// 动态成员
interface Cache {
[key: string]: string
}
const cache: Cache = {}
cache.foo = 'foo'
cache.bar = 'bar'
cache.baz = 'baz'
```
#### 类 (Classes)
- 描述一类具体事物的抽象特征
- Typescript增强类ES6中class的相关语法
- 访问修饰符
- 只读属性
```javascript
class Person {
// ES2017中新增
name: string// = 'init name'
age: number// = 11
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHi(msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
}
}
// ==================================================
// 访问修饰符
class PersonSync {
// ES2017中新增
public name: string// = 'init name' 公有成员 默认 建议加 都可以访问
private age: number// = 11 私有属性 只能类的内部访问 不能在外部和子类中访问
protected gender: boolean //受保护的 只能在子类中访问
// protected constructor(name: string, age: number) { //protected constructor 不允许实例化 允许继承
constructor(name: string, age: number) { //protected constructor 不允许实例化 允许继承
this.name = name
this.age = age
this.gender = true
}
sayHi(msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
}
}
// protected constructor 不允许实例化
const tom = new PersonSync('tom', 18)
console.log(tom.name)
// 属性“age”为私有属性,只能在类“PersonSync”中访问。
// console.log(tom.age)
// 属性“gender”受保护,只能在类“PersonSync”及其子类中访问。
// console.log(tom.gender)
// constructor 允许继承
class Student extends PersonSync {
private constructor(name: string, age: number) {// private constructor 不能被实例化 不能被继承
super(name, age)
console.log(this.gender, 'gender111')
}
static create(name: string, age: number){
return new Student(name, age)
}
}
// constructor 不允许实例化
// const student = new Student('liuchao', 10)
// private constructor
const student = Student.create('liuchao', 10)
```
```javascript
class Person {
// ES2017中新增
public name: string// = 'init name' 公有成员 默认 建议加 都可以访问
private age: number// = 11 私有属性 只能类的内部访问 不能在外部和子类中访问
protected readonly 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}`)
}
}
const tom = new Person('tom', 18)
console.log(tom.name)
```
```javascript
// 抽象类(Abstract Classes)
// 约束子类必须有某一个成员, 不同于接口(interface, 只是成员的抽象, 不包含成员的实现), 抽象类包含成员的实现
// 只能被继承 不能被实例
export {}
abstract class Animal {
eat(food: string): void{
console.log(`呼噜噜的吃: ${food}`)
}
abstract run(distance: number): void // 抽象方法 abstract function 不需要方法提 子类必须实现
}
class Dog extends Animal{
run(distance: number):void{
console.log(`四脚爬行: ${distance}`)
}
}
const a = new Dog()
a.eat('apple')
a.run(2000)
```
#### 类与接口
```javascript
// interface EatAndRun {
// eat(food: string): void
// run(distance: number): void
// }
interface Eat {
eat(food: string): void
}
interface Run {
run(distance: number): void
}
class Person implements Eat, Run {
eat(food: string): void{
console.log(`优雅的进餐: ${food}`)
}
run(distance: number): void{
console.log(`直立行走: ${distance}`)
}
}
class Animal implements Eat, Run {
eat(food: string): void{
console.log(`呼噜噜的吃: ${food}`)
}
run(distance: number): void{
console.log(`爬行: ${distance}`)
}
}
```
#### 泛型(Generics)
- 定义函数接口或类的时候没有具体类型, 使用的时候传递具体类型
```javascript
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
}
function createArray<T>(length: number, value: T): T[]{
const arr = Array<T>(length).fill(value)
return arr
}
const res = createNumberArray(3, 1000)
console.log(res)//=> [1000, 1000, 1000]
const res1 = createArray<string>(4, '200')
console.log(res1)
```
#### 类型声明(Type Declaration)
```javascript
import { camelCase } from 'lodash'
import qs from 'query-string'
qs.parse('?key=value&key2=value2')
// 未执行npm install @types/lodash 自己声明的类型声明
// declare function camelCase(params:string): string
const res = camelCase('hello typed')
```