TypeScript语言
TypeScript 是 JavaScript 的超集,包含了 JavaScript 的所有元素,并扩展了语法。 TypeScript比JavaScript多了类型系统和ES6+ 但是最终都会编译成JavaScript 。TypeScript是强类型的。
TypeScript使用
// 使用typescript 先安装模块:yarn add typescript --dev
// 然后就可以使用tsc的命令来运行文件 yarn tsc 文件名 来编译ts文件
// 也可以使用tsc来编译整个项目;在编译项目之前需要创建一个配置文件:yarn tsc --init```
Flow静态类型检查方案
- flow初步使用
yarn add flow-bin --dev
yarn flow init
// a.js文件 flow文件需要在开始的地方加上注释@flow
// @flow
function sum (a:number, b:number) {
return a + b
}
sum(20, 20)
在终端输入:yarn flow a.js
- flow编译去掉注解方法
方法1:
yarn add flow-remove-types --dev
yarn flow-remove-types src -d dist // 当前目录转换到输出目录
方法2
yarn add @babel/core @babel/cli @babel/preset-flow --dev
// 安装上面的依赖之后还需穿件.babelrc配置文件;增加内容为
{
"presets": ["@babel/preset-flow"]
}
可以使用vscode提供的插件 Flow Language Support 来自动在代码编译阶段就检查。
- 类型推断
// @flow
function square (n) { // 没有类型注解
return n * n
}
square(10) // 100
square('10') // 报语法错误;因为没有定义类型注解;推断为number
- 类型注解
// @flow
function foo ():number { // 没有返回值时类型标注为 void
return 100
}
let num: number = 100
num = 'string' // 报语法错误
5.原始类型数据
const a: string = 'foobar'
const b: number = 100 // NaN Infinity
const c: boolean = true // false
// 前面三个是可以为空的 在非严格模式下是可以的;严格模式下是不行的 strict
const g: string = null
const d: null = null
// 一般会在函数没有返回值的时候用来标记这个函数返回值类型
const e: void = undefined // 在严格模式下只能是undefined;非严格模式下可以是null
const m: undefined = undefined
const f: symbol = Symbol()
Promise
- 数组类型
// 数组类型
const arr1: Array<number> = [1,2,3]
const arr2: number[] = [1,2,3]
// 案例
function sum(...args) {
// 原来的写法需要判断是不是数字类型
return args.reduce((prev,current) => prev + current,0)
}
function sum1(...args:number[]) {
// 原来的写法需要判断是不是数字类型;现在直接可以使用类型注解
return args.reduce((prev,current) => prev + current,0)
}
sum1(1,2,3,4)
- 对象类型
// object 不单单指对象在这里;指的是除原始类型以外的其他类型
const foo: object = {} // [] // function()
// 对象自变量
const obj: { foo: number, bar: string } = { foo: 234, bar: 'hello' }
- 函数类型
// 函数的注解直接在参数后面添加就行;函数返回值的注解就在参数圆括号后面写;如果参数是可选参数就需要在参数后面加上一个?,代表可选参数
function fnc1(a: number, b?: number, c: number = 10, ...rest: number[]): string {
return 'fnc1'
}
fnc1(100)
fnc1(100, 200)
const func2: (a: number, b: number) => string= function (a: number, b: number): string {
return 'func2'
}
- 任意类型
function stringify(value: any) {
// 把value序列化成一个json字符串
return JSON.stringify(value)
}
stringify(100)
stringify('string')
stringify(true)
let foo: any = 'string'
foo = true
// any是不安全的
// 隐式类型推断
let age = 19
// age = '19' // 这里会报错;因为ts已经推断为age为number类型
let foo1 // 如果我们没有咋声明的时候赋值;相当于就是any任意类型的值
foo1 = 'string'
- 特殊类型
// @flow
// 字面量类型
const a: 'foo' = 'foo'
// 字面量配合联合类型 ‘|’
const tyoe: 'success' | 'warning' | 'danger' = 'success'
// 联合类型 ‘|’用法
const b: number | string = 100
// type 关键词单独声明
type StringOrNumber = string | number
const b: StringOrNumber = 'string' // 100
// maybe 类型
const gender: ?number = null
const gender: number | null | void = null
// Mixed 类型(强类型)
function passMixed (value: mixed) {
if (typeof value === 'string') {
value.substr(1)
}
if (typeof value === 'number') {
value * value
}
}
passMixed('string')
passMixed(100)
// Any 类型(弱类型)
function passAny (value: ang) {
value.substr(1)
value * value
}
passAny('string')
passAny(100)
TypeScript和上面的flow类似
- 枚举
// 可以使用enum声明一个枚举;加上const就是常量枚举
// enum PostStatus {
// // 具体的值;会累加;如果给第一个设置的值是5;第二个就是6;依次累加;如果枚举的值是字符串,就需要给每个成员初始化一个字符串的值。
// Draft = 0,
// Unpublished = 1,
// Published = 2
// }
const enum PostStatus {
// 具体的值;会累加;如果给第一个设置的值是5;第二个就是6;依次累加;如果枚举的值是字符串,就需要给每个成员初始化一个字符串的值。
Draft = 0,
Unpublished = 1,
Published = 2
}
const post = {
title: 'Hello TypeScript',
content: 'TypeScript is a typed superset of JavaScript.',
status: PostStatus.Draft //3 // 2 // 1 // 0 这个值可以是0,1,2,3的中的一个;代表状态;这个时候就可以使用枚举来做;好处是:可以给一组数值都分别取上一个更好理解的名字;一个枚举中只会存在几个固定的值;不存在超出范围的可能性
}
// PostStatus[0] // Draft 可以取到Draft这个键
// 当我们确定不会存在通过所引来获取枚举的时候可以使用常量枚举 当我们使用常量枚举的时候就是在枚举前面加上const
- 类型断言
const nums = [110, 120, 119, 112]
const res = nums.find(i => i > 0) // 寻找第一个大于0的数字
// res 我们是知道他肯定是一个数字;但是ts认为可能是数字;也可能找不到;是undefined;所以这个时候就需要用到类型断言
const squer = res * res
// 类型断言
const num1 = res as number
const num2 = <number>res // JSX下不能使用
- 类型声明
// 在定义的时候没有声明;但是在使用的时候可以单独声明;可以兼容很多js模块(第三方模块)
import { camelCase } from 'lodash' // 只安装lodash的话就需要下面的声明declare;如果安装了yarn add @types/lodash --dev 就不用下面的declare来声明了
import qs from 'query-string' // 像这个就自动有类型声明文件直接导入就行了。
// 声明类型 declare关键字
// declare function camelCase(input: string): string
// 转成驼峰式
const res = camelCase('hello typed')
- 元组类型
const tuple:[number,string] = [10, 'sfy']
// 访问元组中的元素
const age = tuple[0]
const name = tuple[1]
const [age1,name1] = tuple
console.log(age);
console.log(age1);
console.log(name);
console.log(name1);
// ES2017新方法:Object.entries({foo:123,bar:456}) 来得到键值数组;其中每一个键值就是一个元组
- 类与接口
// 定义接口
interface Eat {
// 定义成void;就是undefined 在费严格模式下;严格模式下就是null
eat(food: string): void
}
interface Run {
run(distance: number): void
}
// 类中使用这个接口
class Person implements Eat, Run {
// 当函数没有返回值的时候我们应该把他的类型标记为void
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}`);
}
}
- 接口
// 定义接口
interface Post {
title: string
content: string
// 可选成员:在后面加上一个 ?
subtitle?: string
// 添加一个只读成员 一个文章中;summary文章摘要;不能修改是只读属性
readonly summary: string
}
function printPost(post: Post) {
console.log(post.title);
console.log(post.content);
}
printPost({
title: 'hello TypeScript',
content: '你好啊;我是sfy',
summary: 'a JavaScript'
})
// 总结:接口就是来约束对象的结构;要实现这个接口;那么这个对象必须要有这个接口所约束的所有成员
// ----------------动态成员的用法
// 例如缓存对象;在运行的时候就会存在一些动态的键值
interface Cache {
[key: string]: string
}
const cache: Cache = {}
cache.foo = 'value12'
cache.foo1 = 'caldlkjglk'
- 抽象类
// 定义抽象类 abstract
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('aiyou')
d.run(40)
// 泛型:就是指的是我们在定义函数接口类的时候没有指定具体的类型;在使用的时候在指定
function createNumberArray(length: number, value: number) {
const arr = Array<number>(length).fill(value)
}
const res = createNumberArray(3, 100)
function createArray<T>(length: number, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
const res1 = createArray<string>(3, 'foo')
- 类的使用
class Person {
// 在ts中类的属性必须要有个初始值;要不在定义的时候给出默认值;要不在构造函数里面写入初始值
name: string
// 我们可以在类中添加访问修饰符:例如添加private之后就是代表着私有的;默认都是共有成员public
private age: number // 私有属性
// protected代表受保护的访问修饰符 ;也不能在外部访问;但是可以在子类中访问对应的成员
// readonly 代表着只读属性
protected readonly gender: boolean
constructor(name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi(msg: string) {
console.log(`I am ${this.name},${this.age}岁`);
console.log(this.age);
}
}
class Student extends Person {
constructor(name: string, age: number) {
super(name, age)
console.log(this.gender);
}
}
const tom = new Person('tom', 18)
console.log(tom.name);
// console.log(tom.age); // 这里会报错;因为age是私有属性