TypeScript

#### 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')

```

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349