概述
- 解决JavaScript类型系统的问题
- 大大提高代码可靠程度
强类型与弱类型
-
类型安全
-
强类型:
- 语言层面限制函数的实参类型必须与形参类型相同
- 有更强的类型约束
- 不允许任意的隐式类型转换
弱类型:
- 语言层面不限制实参类型
- 几乎没有约束
- 允许任意的隐式类型转换(理解)
JavaScript不是强类型!不然编译过程就报错了
python是强类型
- 变量类型允许随时改变的特点,不是强弱类型的差异
静态类型与动态类型
-
类型检查
-
静态类型:变量声明时它的类型就是明确的,声明后类型就不允许再修改
动态类型:
- 运行阶段才能够明确变量类型,也可以随时变化(区别)
- 变量是没有类型的,存放的值是有类型的
JavaScript自有类型系统的问题
- 弱类型 / 动态类型
- JavaScript没有编译环节
JavaScript的短板
弱类型的问题
-
必须等到运行阶段才能发现一些编译异常。异步
-
类型不明确,函数功能有可能发生改变
-
对象索引器错误的用法
需要每个人约定规则
强类型优势
- 错误更早暴露
- 代码更智能、编码准确
- 重构更牢靠
- 减少不必要的类型判断
Flow静态类型检查方案
Flow:JavaScript的类型检查器。标记类型注解
上手
yarn add flow-bin --dev
添加//@flow
yarn flow init
yarn flow stop
yarn flow
编译移除注解
flow只在编码阶段去找问题,结束之后可以自动移除类型注解
yarn flow-remove-types src -d dist
使用babel也可以移除
yarn add @babel/core @babel/cli @babel/preset-flow --dev
//.babel
{
"presets":["@babel/preset-flow"]
}
yarn babel src -d dist
工具开发插件
类型推断
类型注解
let a :number = 100
原始类型
const a: string = 'fooer'
const b: number = Infinity //NaN//100
const c: boolean = false
const d: null = null
const e: void = undefined
const f: symbol = Symbol()
数组类型
const arr1: Array<number> = [1,3,4]
const arr2: number[] = [2,3,4]
//元组:固定长度的数组,一个函数返回多个返回值的时候使用
const foo: [string, number] = ['foo', 100]
对象类型
const obj1: {foo?: string, bar: number} = {
foo: 'string',
bar: 100
}
//?可有可无
函数类型
函数中的参数和返回值都可以设置类型注解
function foo(callback : string, number => void) {
callback('string', 100)
}
foo(function (str, n){
})
特殊类型
-
字面量类型
const a: 'foo' = 'foo' const type: 'success' | 'warning' | 'danger' = 'success' -
声明类型
type StringOrNumber = string | number const b: StringOrNumber = 'string' // 100 -
Maybe类型
const gender: ?number = undefined //有可能,可以是null或undefined或number
Mix和Any
// 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兼容老代码
运行环境API
const element: HTMLement| null = document.getElementById('app')//一定要传入字符串
TypeScript语言规范与基本应用
概述
-
JavaScript的超集(superset)/ 扩展集
-
使用新特性,兼容性好
-
任何一种JavaScript运行环境都支持
-
功能更为强大,生态更健全,更完善
-
Angular/Vue.js3.0
-
缺点:
- 很多概念 接口 泛型 枚举 / 但是渐进式的
- 项目初期增加成本
上手
yarn init --yes
yarn add typescript --dev//.bin/tsc帮助编译typescript代码
新建ts文件
yarn tsc .\src\01-getting-started.ts//转换为标准的ECMAScript3代码
配置文件
//自动生成配置文件
yarn tsc --init
原始类型
和flow相同
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()
标准库声明
- 标准库就是内置对象所对应的声明
//tsconfig.json
"lib": ["ES2015", "DOM", "ES2017"]
//lib去指定所引用的标准库
中文错误消息
yarn tsc --locale zh-CN
作用域问题
-
默认文件中的成员会作为全局成员
-
多个文件中有相同成员就会出现冲突
-
解决:
-
立即执行函数 / IIFE 提供独立作用域
(function () { const a = 123 })() -
在当前文件使用 export,也就是把当前文件变成一个模块,模块有单独的作用域
export {} //一个语法,变成局部成员
-
Object类型
-
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]
function sum (...args: number[]){
//prev上一次计算结果,current本次循环当前值
reuturn args.reduce((prev, current) => prev + current, 0)
}
sum(1,2,3)
元组类型
- 明确元素数量和元素类型的数组
const tuple: [number, string] = [18, 'zce']
const [age, name] = tuple
//获取每一个键值数组
const entries: [string, number][] = Object.entries({
foo: 123,
bar: 456
})
枚举类型
- 模拟实现枚举
enum P {
a = 0,
b = 1,
c = 2
}
enum P {
a,
b,
c
}//默认从0开始累加,但字符串无法自动累加,需要手动去定义每个枚举变量
status:P.a
- 会入侵到运行时的代码,影响编译后的结果,枚举最后会被编译成一个双向键值对的对象,以便使用所引器去使用枚举
- 使用常量枚举const
函数类型
- 形参和实参必须相同
- 可选参数 / ?
- 参数默认值 / 可有可无 / 必须出现在最后
- 任意个数的参数 ...rest
任意类型
Any Type
- typescript不会对any类型进行类型检查
隐式类型推断
- 无赋值,默认any
最好还是自己给每个变量添加类型
类型断言
-
对于一个一定会返回某类型的函数 / 变量,就可以断言
const num = res as number const num = <number>res//jsx下不能使用 -
不是类型转换,是在编译过程的概念
接口
-
约定一个对象中有具体有什么成员和什么类型
-
约束对象的结构,使用对象去实现一个接口,就必须拥有接口中约定的所有成员
interface Post { name : string content: string } -
可选成员,标记类型是undefined
{ name?:string } -
只读成员,设定后不能再修改
{ readonly name:string } -
动态成员,适用动态成员对象,如缓存对象
interface Cache { [key: string]: string } const cache: Cache = {} cache.foo = 'name1' cache.bar = '2' //动态添加成员,都必须是string类型的键值
类
- 描述一类具体事物的抽象特征
- 用来描述一类具体对象的抽象成员
- ES6开始
- 在类里初始化成员一定要赋值
访问修饰符
可访问级别
public //默认
private
protected//子类可以访问
只读属性
readonly只读属性,初始化后就不能再被修改
类与接口
implement类实现接口
抽象类
abstract不包含具体的实现,大类使用抽象类
- 只能被子类继承,不能被new创造实例对象
- 抽象方法,不需要方法
泛型
-
复用代码,解决冗余
-
不明确类型变成一个参数
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')
类型声明
本文首发于我的GitHub博客,其它博客同步更新。