TypeScript解决了JavaScript类型系统的问题,大大提高代码的可靠程度
强类型 vs. 弱类型 (类型安全)
强类型
- 强类型语言层面限制函数的实参类型必须与形参类型相同
- 强类型语言中不允许任意的隐式类型转换
- 错误更加早暴露
- 代码更智能,编码更准确
- 重构更牢靠
- 减少不必要的类型判断
弱类型
- 弱类型语言层面不会限制实参的类型
- 弱类型语言则允许任意的数据隐式类型转换
非权威说法,没有权威说法
静态类型 vs. 动态类型 (类型检查)
静态类型语言
- 一个变量声明的时候,它的类型就是明确的,之后不允许在修改
动态类型语言
- 在运行阶段才能够明确变量的类型,之后也可以随时改变
- 换言之,动态类型语言的变量是没有类型的,变量中存放的值是有类型的
JavaScript 类型系统特征
- 弱类型
- 动态类型
- 缺失了类型系统的可靠性
- JavaScript 没有编译环节
JavaScript 弱类型产生的常见问题
const obj = {}
obj.foo() // 运行时才发现错误,而非类型检查阶段
setTimeout(() => {
obj.foo() // 运行时才发现错误,而非类型检查阶段
}, 100000)
function sum (a, b) {
return a + b
}
console.log(sum(100, 100))
console.log(sum('100', 100)) // 发生隐式类型转化,但不会报错提示
const obj = {}
obj[true] = 100
console.log(obj['true']) // 虽然可以获取,前后形式不一样,让人感到奇怪
Flow
Flow是JavaScript的类型检查器
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 数组类型
/**
*@flow
*/
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 3, 4]
// 元组
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
}
const obj3: { [string]: string } = []
obj3.key1 = 'dsad'
obj3.key2 = 'dsadsad'
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' // 100
const b: StringOrNumber = 'string' // 100
const gender: ?number = undefined
const gender: number | null | void = undefined
Flow Mixed 与 Any
/**
*@flow
*/
function passMixed (value: mixed) {
if (typeof value === 'string') {
value.substr(1)
}
}
function passAny (value: any) {
value.substr(1)
}
TypeScript
TypeScript 是 JavaScript 的超集
-
JavaScript + ES6+ + 类型系统 = TypeScript (最终编译成) JavaScript
-
任何一种 JavaScript 运行环境都支持
-
相比 Flow, TypeScript 功能更为强大, 生态更健全、更完善
-
TypeScript 已成为 前端领域中的第二语言
TypeScript 原始类型
const a: string = 'foobar'
const b: number = 100
const c: boolean = true
const e: void = undefined
const f: null = null
const g: undefined = undefined
const h: symbol = Symbol()
TypeScript 作用域问题
// ts1.ts
/**
(function () {
console.log(a)
})()
**/
const a {}
export {}
// ts2.ts
const a = 234
export {}
TypeScript Object 类型
原始类型以外的其他类型,不仅仅是对象
const foo: Object = function () {} // [] // {}
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }
TypeScript数组类型
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [4, 5, 6]
// ___________________________________________________
function sum (...args: number[]) {
return args.reduce((pre, current) => pre + current, 0)
}
sum(1, '2', 3) // Argument of type 'string' is not assignable to parameter of type 'number'.ts(2345)
sum(2, 3, 4)
TypeScript 元组类型
const tuple: [number, string] = [18, 'saj']
const age = tuple[0]
const name = tuple[1]
const [age, name] = tuple
TypeScript 枚举类型
枚举会影响编译后的结果,最终编译成双向的键值对对象
enum PostStatus {
Draft = 1
dsadad, // 2
vdxv, // 3
}
/** 其他逻辑 **/
// 编译后生成的js
var PostStatus;
(function (PostStatus) {
PostStatus[PostStatus["Draft"] = 1] = "Draft";
PostStatus[PostStatus["dsadad"] = 2] = "dsadad";
PostStatus[PostStatus["vdxv"] = 3] = "vdxv"; // 3
/** 其他逻辑 **/
})(PostStatus || (PostStatus = {}));
// ---------------------------------------------
// 常量枚举就不会生成双向的键值对对象
const enum PostStatus {
Draft = 1
dsadad, // 2
vdxv, // 3
}
// 编译后的js
var post = {
status: 1 /* Draft */
};
TypeScript 函数类型
对函数的输入输出类型约束
function func1 (a: number, b: number, ...rest: number[]): string {
return (a + b).toString()
}
const func2: (a: number, b: number) => string = function (a: number, b: number): string {
return (a + b).toString()
}
TypeScript 任意类型
function stringify (value: any) {
return JSON.stringfy(value)
}
TypeScript 隐式类型推断
let age = 18 => let age: number = 18
age = 'string' // Type 'string' is not assignable to type 'number'.ts(2322)
TypeScript 类型断言
const nums = [110, 120, 119, 112]
const res = nums.find(i => i > 0)
const num1 = res as number
const num2 = <number>res // jsx 不能使用
TypeScript 接口 (类型约束,没有生成具体代码)
interface Post {
title: string
content?: string // 可选成员
readonly summary: string // 只读成员
[key: string]: string // 动态属性
}
function printPost (post: Post) {
console.log(post.title)
}
printPost({
title: 'Hello'
})
TypeScript 类的基本使用
增强了 class 的相关语法
class Person {
public name: string // 默认公有属性
private age: number // 私有属性 // 只允许在当前类访问,实例和子类不能访问
protected readonly gender: boolean // 受保护属性,只允许在子类中访问
private constructor (name: string, age: number) {
this.name = name // 必须初始化
this.age = age // 必须初始化
this.gender = true
}
sayHi (msg: string): void {
console.log(this.name, msg)
}
static create (name: string, age: number) {
return new Person(name, age)
}
}
TypeScript 类和接口
接口功能尽可能单一,让类去继承多个接口
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)
}
}
TypeScript 抽象类
抽象类可以包含具体实现, 只能被继承,不能创建实例
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)
}
}
TypeScript 泛型
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
}
TypeScript 类型声明
很多第三方模块都有类型声明文件,如果没有,就用declare创建一个类型类型声明模块
import { camelCase } from 'lodash'
declare function camelCase(params: string): string
const res = camelCase('hello typed')