TypeScript解决JavaScript类型系统的问题吗,大大提高代码的可靠程度
TypeScript是JavaScript的超集(扩展JavaScript语法,提供更强大的类型系统),会被编译为JavaScript类型
缺点:
- 语言本身多了很多概念,增强学习成本(TypeScript属于渐进式)
- 项目初期,TypeScript会增加一些成本(长久来说很重要)
TypeScript安装
- yarn init --yes初始化项目
- yarn add typeScript --dev 作为开发依赖,此时node_modules/.bin/tsc文件用于编译typeScript
- yarn tsc xxx.ts 进行编译
- yarn tsc --init生成.tsconfig.json文件
- 按需修改配置文件(rootDir为ts文件目录,outDir为输出js文件目录)
- 此时直接使用yarn tsc就可进行指定目录编译
TypeScript使用指南
-
原始类型
const a: string = 'foobar' const b: number = 100 // NaN Infinity const c: boolean = true // false // 在非严格模式(strictNullChecks)下, // string, number, boolean 都可以为空 // const d: string = null // const d: number = null // const d: boolean = null const e: void = undefined const f: null = null const g: undefined = undefined // Symbol 是 ES2015 标准中定义的成员, // 使用它的前提是必须确保有对应的 ES2015 标准库引用 // 也就是 tsconfig.json 中的 lib 选项必须包含 ES2015 const h: symbol = Symbol() // Promise // const error: string = 100
-
作用域问题
// a.js const a: string = 'a' // 默认文件中的成员会作为全局成员 // b.js const a: number = 2 // 无法重新声明块范围变量“a”。 // 解决 // 1.立即执行函数 (function() { const a: number = 2 })() // 2.使用export 把当前文件变为模块 const a: number = 2 export {}
-
Object类型
// 指所有非原始类型 const foo: object = function() {} // 普通对象类型实现 更专业的使用接口类型实现 const obj: { foo: number, bar: string } = { foo: 123, bar: 'a' }
-
数组类型
// 两种定义方式 const arr: number[] = [1,2,3] const arr: Array<number> = [1,2,3] function sum(...args: number[]) { // 如果是 JS,需要判断是不是每个成员都是数字 return args.reduce((prev, current) => prev + current) } sum(1,2,3)
-
元组类型
// 明确元素数量以及每个元素类型的数组 const tuple: [number, string] = [12, 'wang'] const [age, name] = tuple // 编译为js后实际相当于以下代码 // const age = tuple[0] // const name = tuple[1] // Object.entries获取到每个键值对就是元组 固定长度 固定类型 const entries: [string, number][] = Object.entries({ foo: 123, bar: 456 }) const [key, value] = entries[0]
-
枚举类型
// 对象模拟实现枚举 const PostStatus = { Draft: 0, Unpublished: 1, Published: 2 } enum PostStatus { Draft, // 不设置值时默认从0开始递增 相当于Draft = 0,Unpublished = 1,Published = 2 Unpublished, Published } enum PostStatus { Draft = 6, // 相当于Draft = 6,Unpublished = 7,Published = 8 Unpublished, Published } // 常量枚举,不会侵入编译结果 const enum PostStatus { Draft, Unpublished, Published }
-
函数类型
// 函数声明 // 输入输出类型限制 function fun1(a: number, b: number): string { return 'fun1' } fun1(1, 2) // 如果一个参数可有可无 function fun1(a: number, b?: number): string { return 'fun1' } function fun1(a: number, b: number = 2): string { return 'fun1' } // 如果需要传入多个参数 function fun1(a: number, b: number = 2, ...rest: number[]): string { return 'fun1' } // 函数表达式 const fun2: (a: number, b: number) => string = function (a: number,b: number): string { return 'fun2' }
-
任意类型
function stringify(value: any) { return JSON.stringify(value) } stringify('string') stringify(100) stringify(true) let foo: any = 'string' foo = 100 foo.bar() // any时类型不安全的
-
联合类型
// 联合类型 通过|将变量设置为多种值 let strOrNum: string | number = 2 strOrNum = 'sdf' let arr: string[] | number[] arr = [1, 2, 3] arr = ['foo', 'bar', 'baz'] // 函数参数 function add(name: string | string[]): void { }
-
隐式类型推断
let age = 12 // typeScript推断此类型为number age = 'string' // 不能将类型“string”分配给类型“number” // 尽可能给每个变量添加类型
-
类型断言
const nums = [12, 13, 14] const res = nums.find(i => i > 0) // 类型断言 const num1 = res as number // const num1 = <number>res // JSX下不能使用 const square = res * res
-
接口
// 规范或契约 约定一个对象的结构 interface Post { title: string content: string subtitle?: string // 可选成员 readonly summary: string // 只读成员 } function printPost(post: Post) { console.log(post.title) console.log(post.content) } printPost({ title: 'hello', content: 'China' }) // 动态对象 定义时无法知道有哪些具体成员 interface Cache { [prop: string]: string } const cache: Cache = {} cache.foo = 'value1' cache.bar = 'value2'
-
类的基本使用
// 描述一类具体事物的抽象特征 // ES6之前,通过函数 + 原型模拟实现类 // TypeScript增强了class的相关语法 // 访问修饰符 private私有属性 只能在当前访问 public共有属性 protected只能在继承的子类中访问 相比于private允许继承 // readonly 只读属性 不能被修改 class Person { public name:string private age: number protected readonly gender: boolean constructor (name: string, age: numberr) { this.name = name this.age = age } sayHi (msg: string): void { console.log(`I am ${this.name}, ${msg}`) } }
-
TypeScript类与接口
// 使用接口明确强制一个类去符合某种契约 // 接口描述类的公共部分 // 一个接口尽量只定义一个方法 interface Eat { eat(food: string):void } interface Run { run(distance: number): void } class Animal implements Eat, Run { eat(food: string): void { console.log(`大口吃:${food}`) } run(distance: number): void { console.log(`爬行:${distance}`) } } class Person implements Eat, Run { eat(food: string): void { console.log(`细嚼慢咽的吃:${food}`) } run(distance: number): void { console.log(`行走:${distance}`) } }
-
抽象类
// abstract修饰 只能被继承 不能使用new创建实例 // 有抽象方法的类必须被声明为抽象类 // 和接口相似 抽象方法也是定义但不实现 相比接口 抽象类可以包含成员的实现细节 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)
-
泛型
// 声明时不指定类型 调用时在指定类型 // 使用泛型创建可重用的组件 function createStringArray (length: number, value: string): string[] { return Array<string>(length).fill(value) } function createNumberArray(length: number, value: number): number[] { return Array<number>(length).fill(value) } const res = createStringArray(4, 'ff') console.log(res); const res1 = createNumberArray(4, 100) console.log(res1); // 使用泛型的方式 function createArray<T>(length: number, value: T): T[] { return Array<T>(length).fill(value) } const res2 = createArray<string>(3, 'foo') const res3 = createArray<number>(3, 45) console.log(res2); console.log(res3);
-
类型声明
第三方npm模块 这些npm模块不一定通过typeScript编写 无法使用TypeScript类型检查等功能;需将库里的函数和方法体去掉只保留导出类型声明,产出一个描述JavaScript库和模块信息的声明文件,引入此声明文件 就可以借用TypeScript的各种特性来使用库文件
-
引入模块对应的类型声明模块@types/xx
// 安装lodash的类型声明模块就可正常使用 yarn add @types/lodash --dev import { camelCase } from 'lodash' const res = camelCase('hello typed')
-
-
内部已集成类型声明文件
// 内部已经继承了类型声明文件 所以不用在声明 import qs from 'query-string' qs.parse('?key=value&key2=value2')
-
如果没有类型声明模块时,自定义declare 关键字来定义类型
// 以$符号为例 // 需要使用declare关键字来定义它的类型 帮助TypeScript判断我们传入的参数类型 declare const $: (selector: string) => any $('#foo')