TypeScript

一、JavaScript

强类型与弱类型

语言层面限制函数的实参类型必须与形象类型相同,由于这种强弱类型之分 根本不是某一个权威机构的定义,强类型有更强大的类型约束,而弱类型中几乎没有什么约束,强类型中不允许任意的隐式类型转换,而在弱类型语言则允许任意的数据隐式类型转换。

差异:强类型不允许随意的隐式类型转换,而弱类型是允许的。变量类型允许随时改变的特点,不是强弱类型的差异

弱类型的问题

1、异常需要等到运行时才能发现

2、 类型不明确,函数功能可能发生改变

3、对象索引器的错误用法

强类型的优势

  1. 错误更早的暴露;
  2. 代码更智能,编码更准确
  3. 重构更牢靠
  4. 减少不必要的类型判断

静态类型与动态类型

静态类型:一个变量声明时它的类型就是明确的,声明过后,它的类型就不允许再修改了

动态类型:在运行阶段才能够明确变量类型,而且变量的类型随时可以改变。

动态类型语言当中的变量没有类型,变量中存放的值是有类型的,而JavaScript 语言是一门标准的动态类型语言。

一般从类型安全来分,语言可分为强类型和弱类型

从类型检查的角度来看,语言可分为静态类型 和 动态类型

JavaScript 类型系统特征:缺失了类型系统的可靠性,不靠谱

为什么JavaScript 不是强类型/静态类型?

早前的JavaScript 应用简单,其次,JavaScript没有编译环节,所以JavaScript 选择成为弱类型/动态类型,大规模应用下,这种优势就变成了短板

弱类型的问题

1、异常需要等到运行时才能发现

2、 类型不明确,函数功能可能发生改变

3、对象索引器的错误用法

强类型的优势

  1. 错误更早的暴露;
  2. 代码更智能,编码更准确
  3. 重构更牢靠
  4. 减少不必要的类型判断

二、flow

1. Flow是JavaScript类型检查器

// : number 叫做类型注解
function sum (a: number, b: number) {
  return a + b
}
console.log(sum(1, 2))

 2. 如何安装并使用flow

先执行yarn init -y

执行yarn add flow-bin

在代码中第一行添加flow注释:// @flow

在函数中形参后面加上冒号和类型:function sum (a: number, b: number)

执行yarn flow init创建.flowconfig

执行yarn flow

// @flow
// : number 叫做类型注解
function sum (a: number, b: number) {
  return a + b
}
console.log(sum(1, 2))

console.log(sum('100', '100'))

3. 如何移除flow注解

flow官方提供的操

yarn add flow-remove-types --dev

yarn flow-remove-types src -d dist

使用babel配合flow转换的插件:

yarn add @babel/core @babel/cli @babel/preset-flow --dev

.babelr文件:

{
  "presets": ["@babel/preset-flow"]
}

4. 开发工具插件

VsCode中的插件:Flow Language Support

5. Flow支持的类型


/**
 * 原始类型
 * @flow
 */

 const a: string = 'foo'

 const b: number = Infinity // NaN // 100

 const c: boolean = false // true

 const d: null = null

 const e: void = undefined

 const f: symbol = Symbol()

 const arr: Array<number> = [1, 2, 3]

 const arr2: number[] = [1, 2, 3]

 // 元组
 const foo: [string, number] = ['foo', 100]

const obj1: {foo: string, bar: number} = {foo: 'string', bar: 100}

// 问号表示可有可与的属性
const obj2: {foo?: string, bar: number} = {bar: 100}

// 表示当前对象可以添加任意个数的键,不过键值的类型都必须是字符串
const obj3: {[string]: string} = {}
obj3.key1 = 'value1'
// obj3.key2 = 100

function fn (callback: (string, number) => void) {
  callback('string', 100)
}

fn(function (str, n) {

})

const fo: 'foo' = 'foo'

// 联合类型,变量的值只能是其中之一
const type: 'success' | 'warning' | 'danger' = 'success'

// 变量类型只能是其中的一种类型
const g: string | number = 100

type StringOrNumber = string | number
const h: StringOrNumber = 'stri' // 100

// maybe类型 加一个问号,变量除了可以接受number类型以外,还可以接受null或undefined
const gender: ?number = null
// 相当于
// const gender: number | null | void = undefined

// Mixed / Any  mixed是强类型,any是弱类型,为了兼容老代码,是不安全的,尽量不用any
// string | number | boolean |...
function passMixed (value: mixed) {

}
passMixed('string')
passMixed(100)

function passAny (value: any) {

}
passAny('string')
passAny(100)

const element: HTMLElement | null = document.getElementById('root')

三、TypeScript

TypeScript:JavaScript的超集/扩展集

1. 安装并使用typescript模块

yarn add typescript --dev

创建一个扩展名为ts的文件, myTypeScript.ts:

// TypeScript 可以完全按照JavaScript 标准语法编码
const hello = (name: string) => {
  console.log(`hello, ${name}`)
}

hello('TypeScript')
// hello(111)

执行命令 yarn tsc myTypeScript.ts, 会生成一个同名的js文件

查看myTypeScript.js文件:

// TypeScript 可以完全按照JavaScript 标准语法编码
var hello = function (name) {
    console.log("hello, " + name);
};
hello('TypeScript');
// hello(111)

2. tsc命令的作用

tsc:(typescript compiler) 编译ts文件: 执行命令yarn tsc myTypeScript.ts

检查类型使用异常
移除注解之类的扩展语法
自动转换ECMAScript的新特性
tsc编译整个项目:

执行命令yarn tsc --init,生成tsconfig.json文件
执行命令yarn tsc, 按照配置文件将src中的ts文件生成到了dist中的js文件,并且是采用ES2015语法

3. TS支持的原始类型

const a: string = 'foobar'

const b: number = 100 // NaN Infinity

const c: boolean = true // false

// const d: boolean = null // 严格模式下不支持赋值null

const e: void = undefined // 函数没有返回值时的返回值类型

const f: null = null

const g: undefined = undefined

const h: symbol = Symbol()

4. TS标准库声明

标准库就是内置对象所对应的声明

在tsconfig.json中写上:

"lib": ["ES2015", "DOM"], 

5. 中文错误消息

yarn tsc --locale zh-CN

6. 作用域

每个文件都是全局作用域,所以在不同文件中定义同名变量会报错,解决方案:

         使用立即执行函数,产生作用域

(function () {
  const a = 123
} )()

使用export

const a = 11
export {} // 确保跟其他实例没有成员冲突

7. Object类型

TypeScript中的Object类型泛指所有的的非原始类型。如对象、数组、函数.

object类型并不单指对象,而是指除了原始类型之外的其他类型.

对象的赋值必须与定义的属性保持一致,不能多也不能少。更专业的写法是用接口.

export {} // 确保跟其他实例没有成员冲突

const foo: object = function () {} // [] // {} 

const obj: {foo: number, bar: string} = {foo: 123, bar: 'string'} 

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)
}
sum(1, 2, 3)

8. 元组类型

固定长度的数据。 例如Object.entries(obj)的返回值里面的每一个元素都是一个元组

export {}

const tuple: [number, string] = [19, 'jal']
// 下标取值
// const age = tuple[0]
// const name = tuple[1]

// 数组解构
const [age, name] = tuple

9. 枚举类型

// JS中没有枚举类型,则使用对象模拟枚举类型
// const PostStatus = {
//   Draft: 0,
//   Uppublished: 1,
//   Published: 2
// }

// 枚举类型。使用时和对象属性一样
// 如果不指定值,则从0开始累加。如果制定了第一个成员的值,后面的成员则再第一个成员基础上累加。值如果是字符串,就得指定具体的值
const enum PostStatus {
  Draft = 0, 
  Uppublished = 1,
  Published = 2
}

const post = {
  title: 'Hello TypeScript',
  content: 'Type...',
  status: PostStatus.Draft
}

10. 函数类型

// 获取不确定参数
// function func1 (a: number, b: number): string {
// function func1 (a: number, b?: number): string {
// function func1 (a: number, b: number = 10): string {
function func1 (a: number, b: number = 10, ...rest: 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 'f'
}

11. 任意类型

any类型是为了兼容老的代码,它还是动态类型,是不安全的,尽量少用

function stringify (value: any) {
  return JSON.stringify(value)
}

stringify('string')
stringify(100)
stringify(true)

let foo: any = 'string'

foo = 100

foo.bar()

12. 隐式类型推断

let age = 18 // ts推断出类型是number
// age = 'str' // 会报错 不能将类型“"str"”分配给类型“number”。

let foo // 此时无法推断具体类型,foo则是动态类型,any类型
foo = 1 // 不会报错
foo = 'string' // 不会报错

13. 类型断言

const nums = [110, 120, 119, 112]

const res = nums.find(i => i>0)
// const res: number | undefined
// const square = res * res

const num1 = res as number // 断言 res 是number
const square = num1 * num1
const num2 = <number>res // 或者这种方式。JSX下不能使用

14. 接口

// 可以用分号分割,分号可以省略
interface Post {
  title: String
  content: String
}

function printPost (post: Post) {
  console.log(post.title)
  console.log(post.content)
}

printPost({
  title: 'hello',
  content: 'javascript'
})

可选属性、只读属性

interface Post {
  title: String
  content: String
  subtitle?: string // 可有可无的属性。也就是说该属性为string或者undefined
  readonly summary: string
}

const hello: Post = {
  title: 'hello',
  content: 'javascript',
  summary: 'js'
}

//报错: Cannot assign to 'summary' because it is a read-only property.
// hello.summary = '11'

动态属性

interface Cache {
  // 动态成员
  [prop: string]: string
}

const cache: Cache = {}

cache.foo = 'ff'

15. 类

TypeScript增强了class的相关语法

类的基本使用

class Person {
  // ES2017定义的语法:
  name: string // = 'init name'
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  sayHi (msg: string): void {
    console.log(`I am ${this.name}`)
  }
}

访问修饰符:private public protected 。默认是public.

export {}

class Person {
  // ES2017定义的语法:
  name: string // = 'init name'
  private age: number
  protected 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}`)
  }
}

const tom = new Person('tom', 18)
console.log(tom.name)
// console.log(tom.age) // 属性“age”为私有属性,只能在类“Person”中访问。
// console.log(tom.gender) // 属性“gender”受保护,只能在类“Person”及其子类中访问。

class Student extends Person {
  constructor(name: string, age: number) {
    super(name, age)
    // 父类的protected属性子类可以访问。
    console.log(this.gender)
  }
}

静态属性、构造器私有化后不能new

class Student extends Person {
  private constructor(name: string, age: number) {
    super(name, age)
    console.log(this.gender)
  }
  static create(name: string, age: number) {
    return new Student(name, age)
  }
}

const jack = Student.create('jack', 18)

只读属性,在属性声明前面加上readonly即可

protected readonly gender: boolean

16. 类与接口

// 尽可能让接口简单。一个接口只约束一个能力,一个类实现多个接口
interface Eat {
  eat (foo: 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}`)
  }
}

17. 抽象类

被abstract修饰,不能被new,只能被继承。继承抽象类的子类,必须实现父类的抽象方法

abstract class Animal {
  eat (food: string) : void {
    console.log(`呼噜呼噜的吃:${food}`)
  }

  // 抽象方法不需要方法体,子类必须要实现抽象方法
  abstract run(distance: number): void
}

// 非抽象类“Dog”不会实现继承自“Animal”类的抽象成员“run”
class Dog extends Animal {
  run(distance: number): void {
    console.log(`四脚爬行:${distance}`)
  }
}

const dog = new Dog()
dog.run(20)
dog.eat('fish')

18. 泛型

把类型作为参数,放在尖括号中

function createNumberArray(length: number, value: number): number[] {
  const arr = Array<number>(length).fill(value)
  return arr
}

const res = createNumberArray(3, 100 ) // [100, 100, 100]

function createArray<T> (length: Number, value: T): T[] {
  const arr = Array<T>(length).fill(value)
}

const arrRes = createArray<string>(3, 'foo') // ['foo', 'foo', 'foo']

19. 类型声明

TypeScript中的扩展名为d.ts的文件就是类型声明文件

import {camelCase} from 'lodash'

// 自己写declare语句声明类型
declare function camelCase (input: string): string

const res = camelCase('zjal')

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值