TypeScript
- TypeScript大大提高了代码的可靠程度
- Javascript自有类型系统的问题
强类型与弱类型
强类型与弱类型(类型安全)
- 强类型: 语言层面限制函数的实参类型必须与形参数类型相同
- 弱类型: 不限制,允许任意的数据隐式类型转换
静态类型与动态类型(类型检查)
- 静态类型: 变量声明时,类型就是明确的,并不能修改
- 动态类型: 运行阶段才能明确变量类型,并能随时改变
js弱且动
- 缺失了类型系统的可靠性
- 是脚本语言,没有编译环节
弱类型的问题
setTimeout(() => {obj.foo() //此处有问题,需要等执行到才能发现}, 1000000)console.log(100+'100');// 100100// 3. 对象索引器的错误用法const obj = {}obj[true] = 100 // 属性名会自动转换为字符串console.log(obj['true'])
强类型的优势
// 强类型的优势// 1. 强类型代码错误更早暴露// 2. 强类型代码更智能,编码更准确// function render (element) {// element.className = 'container'// element.innerHtml = 'hello world'// }// =================================// 3. 重构更可靠// const util = {// aaa: () => {// console.log('util func')// }// }// =================================// 4. 减少了代码层面的不必要的类型判断function sum (a, b) {if (typeof a !== 'number' || typeof b !== 'number') {throw new TypeError('arguments must be a number')}return a + b}
Flow
JavaScript 的类型检查器
function sum (a: number, b: number) {
return a + b
}安装: yarn add flow-bin —dev
运行: yarn flow
初始化配置文件: flow init
退出: flow stop
移除编译注解
- 安装:yarn add flow-remove-types —dev
移除某目录并移动到某目录 (a: number, b: number)
- yarn flow-remove-types . -d dist
- 编译工具 babel 配合插件也能实现自动移除编译注解
- 安装 yarn add @babel/core (babel核心模块) @babel/cli (babel的cli工具,直接使用babel命令编译) @babel/preset-flow (转换flow类型的插件) —dev
添加babel配置文件 .babelrc
- {
“presets”: [“@babel/preset-flow”]
} - yarn babel src -d dist 把src下的目录文件都编译到dist目录当中
- {
- flow类型注解 ```javascript /**
- 类型注解 *
- @flow */
function square (n: number) { return n * n }
let num: number = 100
// num = ‘string’ // error
function foo (): number { return 100 // ok // return ‘string’ // error }
function bar (): void { // return undefined } ```
- Flow 原始类型
/*** 原始类型** @flow*/const a: string = 'foobar'const b: number = Infinity // NaN // 100const c: boolean = false // trueconst d: null = nullconst e: void = undefinedconst f: symbol = Symbol()
- Flow 数组类型
/*** 数组类型** @flow*/const arr1: Array<number> = [1, 2, 3]const arr2: number[] = [1, 2, 3]// 元组const foo: [string, number] = ['foo', 100]
- Flow 数组类型
/*** 对象类型** @flow*/const obj1: { foo: string, bar: number } = { foo: 'string', bar: 100 }const obj2: { foo?: string, bar: number } = { bar: 100 } //foo?可有可无const obj3: { [string]: string } = {} //键和值,必须是字符串obj3.key1 = 'value1'obj3.key2 = 'value2'
- Flow 数组类型
/*** 函数类型** @flow*/function foo (callback: (string, number) => void) {callback('string', 100)}foo(function (str, n) {// str => string// n => number})
- Flow 数组类型
/*** 特殊类型** @flow*/// 字面量类型const a: 'foo' = 'foo' //必须是某值const type: 'success' | 'warning' | 'danger' = 'success' //三选一// ------------------------// 声明类型type StringOrNumber = string | numberconst b: StringOrNumber = 'string' // 100// ------------------------// Maybe 类型const gender: ? number = undefined// 相当于// const gender: number | null | void = undefined
- Flow Mixed Any
/*** Mixed Any** @flow*/// string | number | boolean | ....function passMixed (value: mixed) {if (typeof value === 'string') {value.substr(1)}if (typeof value === 'number') {value * value}}passMixed('string')passMixed(100)// ---------------------------------function passAny (value: any) {value.substr(1)value * value}passAny('string')passAny(100)
- Flow 运行环境 API ```javascript /**
- 运行环境 API *
@flow */
const element: HTMLElement | null = document.getElementById(‘app’) ```
Flow 类型小结
- 类型查询: https://www.saltycrane.com/cheat-sheets/flow-type/latest/
- TypeScript
- 类型查询: https://www.saltycrane.com/cheat-sheets/flow-type/latest/
JavaScript的超集(superset)
任何一种JavaScript运行环境都支持
TypeScript作为一门编程语言,功能更为强在,生态也更健全、更完善
前端领域中的第二语言,长周期的大项目,会建议选择TypeScript
缺点一:语言本身多了很多概念,属于“渐进式”
缺点二:项目初期,TypeScript会增加一些成本
安装:
- 先初始化一个项目中使用的package.json文件,管理项目依赖项: yarn init —yes
- 添加TypeScript模块: yarn add typescript —dev
- tsc编译某文件: yarn tsc XXX.ts 可生成编译后es2013的js文件
- 配置文件,编译整个项目/工程
- 创建typescript的配置文件: yarn tsc —init
- 创建src目录,并把ts文件放入src
- 然后运行 yarn tsc 即可编译
原始数据类型
// 原始数据类型const a: string = 'foobar'const b: number = 100 // NaN Infinityconst c: boolean = true // false// 在非严格模式(strictNullChecks)下,// string, number, boolean 都可以为空// const d: string = null// const d: number = null// const d: boolean = nullconst e: void = undefinedconst f: null = nullconst g: undefined = undefined// Symbol 是 ES2015 标准中定义的成员,// 使用它的前提是必须确保有对应的 ES2015 标准库引用// 也就是 tsconfig.json 中的 lib 选项必须包含 ES2015const h: symbol = Symbol()//解决办法 "target": "ES2015", 或 "lib": [ES2015],// Promise// const error: string = 100//标准库就是内置对象所对应的声明
TypeScript 中文错误信息
- yarn tsc —locale zh-CN
作用域问题
// 作用域问题// 默认文件中的成员会作为全局成员// 多个文件中有相同成员就会出现冲突// const a = 123// 解决办法1: IIFE 提供独立作用域// (function () {// const a = 123// })()// 解决办法2: 在当前文件使用 export,也就是把当前文件变成一个模块// 模块有单独的作用域const a = 123export {}
作用域问题
// Object 类型export {} // 确保跟其它示例没有成员冲突// object 类型是指除了原始类型以外的其它类型const foo: object = function () {} // [] // {}// 如果需要明确限制对象类型,则应该使用这种类型对象字面量的语法,或者是「接口」const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }// 接口的概念后续介绍
数组类型
// 数组类型export {} // 确保跟其它示例没有成员冲突// 数组类型的两种表示方式const arr1: Array<number> = [1, 2, 3]const arr2: number[] = [1, 2, 3]// 案例 -----------------------// 如果是 JS,需要判断是不是每个成员都是数字// 使用 TS,类型有保障,不用添加类型判断function sum (...args: number[]) {return args.reduce((prev, current) => prev + current, 0)}sum(1, 2, 3) // => 6
元组(Tuple)类型
// 元组(Tuple)export {} // 确保跟其它示例没有成员冲突const tuple: [number, string] = [18, 'zce']// const age = tuple[0]// const name = tuple[1]const [age, name] = tuple// ---------------------const entries: [string, number][] = Object.entries({foo: 123,bar: 456})const [key, value] = entries[0]// key => foo, value => 123
枚举类型
// 枚举(Enum)export {} // 确保跟其它示例没有成员冲突// 用对象模拟枚举// const PostStatus = {// Draft: 0,// Unpublished: 1,// Published: 2// }// 标准的数字枚举// enum PostStatus {// Draft = 0,// Unpublished = 1,// Published = 2// }// 数字枚举,枚举值自动基于前一个值自增// enum PostStatus {// Draft = 6,// Unpublished, // => 7// Published // => 8// }// 字符串枚举// enum PostStatus {// Draft = 'aaa',// Unpublished = 'bbb',// Published = 'ccc'// }// 常量枚举,不会侵入编译结果const enum PostStatus {Draft,Unpublished,Published}const post = {title: 'Hello TypeScript',content: 'TypeScript is a typed superset of JavaScript.',status: PostStatus.Draft // 3 // 1 // 0}// PostStatus[0] // => Draft
函数类型
// 函数类型export {} // 确保跟其它示例没有成员冲突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 'func2'}
任意类型
// 任意类型(弱类型)export {} // 确保跟其它示例没有成员冲突function stringify (value: any) {return JSON.stringify(value)}stringify('string')stringify(100)stringify(true)let foo: any = 'string'foo = 100foo.bar()// any 类型是不安全的
隐式类型推断
// 隐式类型推断export {} // 确保跟其它示例没有成员冲突let age = 18 // 已经推断了为number// age = 'string' //不能用 了let foo //推断为anyfoo = 100foo = 'string'// 建议为每个变量添加明确的类型标注
类型断言
// 类型断言export {} // 确保跟其它示例没有成员冲突// 假定这个 nums 来自一个明确的接口const nums = [110, 120, 119, 112]const res = nums.find(i => i > 0)// const square = res * resconst num1 = res as numberconst num2 = <number>res // JSX 下不能使用
接口
// 接口export {} // 确保跟其它示例没有成员冲突interface Post {title: stringcontent: string} //接口就是用来约束对象的结构function printPost (post: Post) {console.log(post.title)console.log(post.content)}printPost({title: 'Hello TypeScript',content: 'A javascript superset'})
// 可选成员、只读成员、动态成员export {} // 确保跟其它示例没有成员冲突// -------------------------------------------interface Post {title: stringcontent: stringsubtitle?: stringreadonly summary: string}const hello: Post = {title: 'Hello TypeScript',content: 'A javascript superset',summary: 'A javascript'}// hello.summary = 'other'// ----------------------------------//动态键值interface Cache {[prop: string]: string}const cache: Cache = {}cache.foo = 'value1'cache.bar = 'value2'
类型断言
// 类(Class)export {} // 确保跟其它示例没有成员冲突class Person {name: string // = 'init name'age: numberconstructor (name: string, age: number) {this.name = namethis.age = age}sayHi (msg: string): void {console.log(`I am ${this.name}, ${msg}`)}}
类的访问修饰符
// 类的访问修饰符export {} // 确保跟其它示例没有成员冲突class Person {public name: string // = 'init name'private age: numberprotected gender: booleanconstructor (name: string, age: number) {this.name = namethis.age = agethis.gender = true}sayHi (msg: string): void {console.log(`I am ${this.name}, ${msg}`)console.log(this.age)}}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 tom = new Person('tom', 18)console.log(tom.name)// console.log(tom.age)// console.log(tom.gender)const jack = Student.create('jack', 18)
类的访问修饰符
// 类的只读属性export {} // 确保跟其它示例没有成员冲突class Person {public name: string // = 'init name'private age: number// 只读成员protected readonly gender: booleanconstructor (name: string, age: number) {this.name = namethis.age = agethis.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
类与接口
// 类与接口export {} // 确保跟其它示例没有成员冲突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) {console.log(`直立行走: ${distance}`)}}class Animal implements Eat, Run {eat (food: string): void {console.log(`呼噜呼噜的吃: ${food}`)}run (distance: number) {console.log(`爬行: ${distance}`)}}
抽像类
// 抽像类export {} // 确保跟其它示例没有成员冲突abstract 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)
泛型
// 泛型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<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, 100)// res => [100, 100, 100]const res = createArray<string>(3, 'foo')
泛型
// 类型声明//安装 yarn add lodash//npm install @types/lodashimport { camelCase } from 'lodash'import qs from 'query-string'qs.parse('?key=value&key2=value2')// declare function camelCase (input: string): stringconst res = camelCase('hello typed')
