目录
类型
- 强类型VS弱类型,强类型不允许隐式转换,弱类型可以;
- 静态类型VS动态类型,静态类型就是声明时就明确了类型,并且一旦声明后类型不能修改;动态类型是运行时才知道类型,并且声明后类型可以被修改;
- Javascript是一种弱类型的动态语言
弱类型缺点
- 类型错误需等到运行时才能抛出异常
- 参数的类型可能导致函数的功能与预期不符
- 对象属性名使用发生错误
var obj = {}; obj[true] = true; console.log(Obj["true"])//true
强类型优点
- 更早发现错误
- 开发更智能,编码更准确,减少不必要的类型判断
- 方便重构
Flow
概念
- Javascript的类型检查器,通过给参数增加类型注解,可以避免参数不明确带来的问题
使用
下载flow-bin
初始化flow: flow init
使用,在文件中加一个注释@flow, 然后在需要的地方添加类型注解
如何去掉类型注解
1. flow官方提供
下载flow-remove-types
使用flow-remove-types xx当前文件相对路径 -d xx去掉类型注解后的文件相对路径
2. babel
下载@babel/cli @babel/core @babel/preset-flow
使用 babel xxx当前文件目录 -d xxx去掉类型注解后的文件目录
- Flow Language Support(VScode提供的flow插件)
- flow会根据使用情况判断类型,最好还是为参数添加类型注解
- 支持的原始类型
/**
* 原始类型
*
* @flow
*/
const a: string = 'foobar'
const b: number = Infinity // NaN // 100
const c: boolean = false // true
const d: null = null
const e: void = undefined//这个在函数中也是一样
const f: symbol = Symbol()
对象类型
/**
* 对象类型
*
* @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 = 'value1'
obj3.key2 = 'value2'
函数类型
/**
* 函数类型
*
* @flow
*/
function foo (callback: (string, number) => void) {
callback('string', 100)
}
foo(function (str, n) {
// str => string
// n => number
})
特殊类型
/**
* 特殊类型
*
* @flow
*/
// 字面量类型
const a: 'foo' = 'foo'
const type: 'success' | 'warning' | 'danger' = 'success'//组合类型
// ------------------------
// 声明类型
type StringOrNumber = string | number
const b: StringOrNumber = 'string' // 100
// ------------------------
// Maybe 类型
const gender: ?number = undefined
// 相当于
// const gender: number | null | void = undefined
mixed和any类型
/**
* Mixed
*
* @flow
*/
//mixed就相当于所有类型的混合,仍然属于强类型 string | number | boolean | ....
function passMixed (value: mixed) {
if (typeof value === 'string') {
value.substr(1)
}
if (typeof value === 'number') {
value * value
}
}
passMixed('string')
passMixed(100)
//any的用法类似,但是any是一种弱类型,是为了兼容老版本而出现的,不建议使用
flow官方文档和类型手册
运行环境API
Typescript
概念
是一种强类型的静态语言,Javascript的超集,在Javascript的语言基础上拓展了一些特性,开发完成后再编译成JavaScript代码,任何一种JavaScript的运行环境都支持
缺点
- 语言本身多了很多概念
- 开发初期,成本较高,但对于大型项目,利大于弊
使用
- 下载包
npm i typescript --save-dev
- 生成tsc命令
(相当于typescript compile),可以使用该命令把一个ts文件编译成一个js文件( tsx xxxx.ts )
- 配置文件
可以使用tsconfig.json文件来配置编译选项(使用 tsc --init 即可生成)
下面是一些主要的配置选项
target: 编译后采用那个es版本
module: 生成模块使用哪种方式
sourceMap:源代码映射
outDir:编译后生成文件目录
rootDir:需要编译的文件目录
strict: 是否启用严格模式
原始类型
// 原始数据类型
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
标准库声明
标准库声明就是内置对象对应声明
比如,Symbol就是es2015的内置对象,因此当你tsconfig.json中lib选项和tatget选项没有es2015,在代码中写入Symbol()就会报错,因为缺少标准库声明,lib中加上标准库es2015就不会报错了
作用域问题
typescript不允许全局作用域中存在相同变量,因此一般把相同的变量放在不同作用域中
一些类型
- 对象类型
// Object 类型
export {} // 确保跟其它示例没有成员冲突
// object 类型是指除了原始类型以外的其它类型
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]
// 案例 -----------------------
// 如果是 JS,需要判断是不是每个成员都是数字
// 使用 TS,类型有保障,不用添加类型判断
function sum (...args: number[]) {
return args.reduce((prev, current) => prev + current, 0)
}
sum(1, 2, 3) // => 6
- 元组类型(固定元素类型和数量)
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
- 枚举类型(某个属性可以有多个确定的值)
js
// 用对象模拟枚举
const PostStatus = {
Draft: 0,
Unpublished: 1,
Published: 2
}
ts
// 数字枚举,枚举值自动基于前一个值自增
enum PostStatus {
Draft = 6,
Unpublished, // => 7
Published // => 8
}
// 字符串枚举
enum PostStatus {
Draft = 'aaa',
Unpublished = 'bbb',
Published = 'ccc'
}
枚举会侵入编译结果,生成双向键值
使用常量枚举可以解决
// 常量枚举,不会侵入编译结果
const enum PostStatus {
Draft,
Unpublished,
Published
}
编译结果:直接生成对应的值
- 函数类型
//函数表达式
const func2: (a: number, b: number) => string = function (a: number, b: number): string {
return 'func2'
}
//函数声明
function func1 (a: number, b: number = 10, ...rest: number[]): string {
return 'func1'
}
func1(100, 200)
- 尽量不要使用任意类型(any)
- 隐式类型推断
let age = 18 // number
// age = 'string' 被推断为number了,此处报错
- 类型断言( as / 尖括号<>)
const num1 = res as number
const num2 = <number>res // JSX 下不能使用
- 接口(interface),约定成员类型, 编译时会忽略掉
interface Post {
title: string
content: string
subtitle?: string//可选
readonly summary: string//只读
}
const hello: Post = {
title: 'Hello TypeScript',
content: 'A javascript superset',
summary: 'A javascript'
}
interface Cache {
[prop: string]: string//动态成员
}
const cache: Cache = {}
cache.foo = 'value1'
cache.bar = 'value2'
class
- 概念
class Person {
public name: string // = 'init name'// 公共成员,可以被继承,可以被外部访问
private age: number//私有成员,不能被继承,不能被外部访问
// 只读成员 readonly
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}`)
console.log(this.age)
}
}
const tom = new Person('tom', 18)
console.log(tom.name)
// tom.gender = false
- 和接口一起使用
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}`)
}
}
- 抽象类
和接口一样去限制类型,但是只能被继承,不能直接new
//抽象类 类之前加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)
}
}
//new 一个子类
const d = new Dog()
//具备抽象方法,以及抽象类中的其他方法
d.eat('嗯西马')
d.run(100)
- 泛型(generic)
声明时把不明确的类型定义<T>,使用的时候传入参数
function createArray<T> (length: number, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
const res = createArray<string>(3, 'foo')
- 类型声明