TypeScript
大大提高代码的可靠程度JavaScript
自有类型系统的问题JavaScript
自有类型系统的问题Flow
静态类型检查方案TypeScript
语言规范与基本应用eg:
class Main {
static void foo(int num) {
System.out.println(num);
}
public static void main(String[] args) {
Main.foo(100); // ok
Main.foo("100"); // error "100" is a string
Main.foo(Integer.parseInt("100 ")); // ok
}
}
function foo(num) {
console.log(num)
}
foo(100) // ok
foo('100') // ok
foo(parseInt('100')) // ok
由于这种强弱类型之分根本不是某个权威机构的定义;
强类型有更强的类型约束,而弱类型中几乎没有什么约束
静态类型与动态类型
var foo = 100
foo = 'bar' // ok
console.log(foo)
也可以说在动态类型语文中的变量没有类型,而变量中存放的值是有类型的
强类型
和 弱类型
静态类型
和 动态类型
弱类型
且动态类型
js的类型用一个词描述:【任性
】 缺失了类型系统的可靠性 --> 不靠谱
1. 早前的 JavaScript 应用简单
2. JavaScript 没有编译环节
3. 选择为 弱类型 / 动态类型
4. 在大规模应用下,这种【优势】就变成了短板
举例
// 01
const obj = {}
obj.foo()
// 报错要等到运行阶段才能发现类型异常
// ======================
// 02
function sum(a, b) {
return a + b
}
console.log(sum(100, 100)) // 200
console.log(sum(100, '100')) // 100100
// ======================
// 03
const obj = {}
obj[true] = 100 // 会自动转换成字符串
console.log(obj['true'])
JavaScript的类型的检查器
Flow只是一个小工具,So Easy
// 类型注解
function sum (a: number, b: number) {
return a + b
}
sum(100, 50) // ok
sum('100', 50) // error
// 安装 yarn add flow-bin --dev
// 增加.flowconfig文件 yarn flow init
/*
.flowconfig
[ignore]
[include]
[libs]
[lints]
[options]
[strict]
*/
// 使用的时候需要在文件顶上增加 @flow,并且需要关闭掉vscode的语法校验
// @flow
function sum (a, b) {
return a + b
}
// 终端使用命令检查
// 执行 yarn flow
// 停止 yarn flow stop
// 所以安装 yarn add flow-remove-types --dev
// 执行 yarn flow-remove-types [文件目录] . -d [输出目录]
// @babel/core 核心模块
// @babel/cli cli工具,可以让我们在命令行中直接使用babel命令去完成编译
// @babel/preset-flow --dev
// 安装
// yarn add @babel/core @babel/cli @babel/preset-flow --dev
// 2. 添加babel的配置文件 .babelrc
/*
// .babelrc
{
"presets": ["@babel/preset-flow"]
}
*/
// 执行命令 yarn babel src -d dist
让错误直接在开发工具上显示出来
/**
* 类型推断
* @flow
*/
function square (n) {
return n * n
}
square('100') // 这里用了字符串,flow会推导出 n*n是错误的
// 1. 函数的参数上以及返参
funcion square(n: number): number {
return n * n
}
// 2. 变量
let num: number = 100;
// 3. 没有返回值标记成void
function say(): void {}
let a: string = 'foobar'
let b: number = Infinity // NaN // 100
let c: boolean = true
let d: null = null
let e: void = undefined
let f: symbol = Symbol()
// 泛型
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
// 元组
const f00: [string, number] = ['foo', 100]
const obj1: {foo: string, bar: number} = { foo: 'string', bar: 100 }
const obj2: {foo?: string, bar: number} = {bar: 100} // foo可选
const obj3: {[string]: string} = {}
obj3.key1 = 'value1'
obj3.key2 = 'value2'
function foo(callback: (string, number) => void) {
callback('string', 100)
}
foo(function(str, n) {
// str => string
// n => number
})
// 字面量类型
const a: 'foo' = 'foo' // a变量只能为 foo字符串
const type: 'success' | 'warning' | 'danger' = 'success'
const b: string | number = 100 // 'string'
type StringOrNumber = string | number
const c: StringOrNumber = 'string' // 100
// maybe 类型
const gender: ?number = null // undefined // number
// 上面等价于下面这个
const gender: number | null | void = undefined // null // number
// mixed 就是所有类型 ==> string | number | boolean | ....
function passMixed(value: mixed) {
}
passMixed('string')
passMixed(100)
// -----------------------
function passAny(value: any) {
}
passAny('string')
passAny(100)
// 区别是 any是弱类型 mixed是强类型
function passMixed(value: mixed) {
value.substr(1)
value * value
// 这里会提示报错
// 改成下面
if (typeof value == 'value') {
value.substr(1)
}
if (typeof value == 'number') {
value * value
}
}
passMixed('string')
passMixed(100)
// -----------------------
function passAny(value: any) {
value.substr(1)
value * value
}
passAny('string')
passAny(100)
const element: HTMLElement | null = document.getElementById('app') // 这里必须传字符串 传数字会提示报错
// https://github.com/facebook/flow/blob/master/lib/core.js
// https://github.com/facebook/flow/blob/master/lib/dom.js
// https://github.com/facebook/flow/blob/master/lib/bom.js
// https://github.com/facebook/flow/blob/master/lib/cssom.js
// https://github.com/facebook/flow/blob/master/lib/node.js
任何一种JavaScript运行环境都支持
TypeScript属于渐进式
yarn init --yes
yarn add typescript --dev
yarn tsc xxxx.ts
// 可以完全按照 JavaScript 标准语法编写代码
yarn tsc --init 生成一个tsconfig.json
compilerOptions
target: 编译之后的ES版本
module: 输出的代码采用什么样的方式进行模块化
lib: 指定所引用的标准库 ["ES2015", "DOM"]
outDir: 设置编译结果输出到的文件夹 dist
rootDir: 配置我们源代码的文件夹 src
sourceMap: 是否开启源代码映射
strice: 是否开启严格模式
需要直接运行tsc命令编译整个项目
yarn tsc
const a: string = 'foobar'
const b: number = 100 // NaN Infinity
const c: boolean = true // false
// 以上是在非严格模式下均可设置为 null
const e: void = undefined
const f: null = null
const g: undefined = undefined
const h: symbol = Symbol()
就是内置对象所对应的声明
const error: string = 100
两个.ts文件使用相同变量量会提示错误
export {}
不单指对象
const foo: object = {} // [] // function() {}
// 可以这样定义指定为对象 可以用接口
const obj = {foo: number } = { foo: 123 }
const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
// -------------------------
function sum(...args: number[]) {
return args.reduce((prev, current) => prev + current, 0)
}
sum(1, 2, 3)
明确元素数量以及每个元素类型的一个数组,类型不必要完全相同
const tuple: [number, string] = [18, 'string']
tuple[0]
tuple[1]
const [age, name] = tuple
// ------------------------
enum OrderStatus {
WaitPay = 0,
Pay = 1,
Cancel = 2
}
// 枚举的值可以是字符串
enum OrderStatus {
WaitPay = 'waitPay',
Pay = 'pay'
}
// 会被编译成以下代码
var OrderStatus;
(function (OrderStatus) {
OrderStatus["WaitPay"] = "waitpay";
OrderStatus["Pay"] = "pay";
})(OrderStatus || (OrderStatus = {}));
// 如果枚举的值是数值的,可以这样取 OrderStatus[0]
// 在enum 的前面加上 const
const enum OrderStatus {
WaitPay = 0,
Pay
}
// 编译的时候会去掉枚举相关的信息,只留下 0 , 1 , 3
输入输出类型限制
function func1(a: number, b: number): string {
return 'func1'
}
// 可选参数 可以设置 a?: number
// -------------------------
// 函数的表示式
const func2: (a: number, b: number) => string = function(a: number, b: number): string {
return 'func2'
}
不会有任何的类型检查
function stringify(value: any) {
return JSON.stringify(value)
}
建议为每个变量添加明确的类型
let age = 18 // 推断为age: number
age = 'string' // 会报错
let foo; // 推断为any
foo = 100
foo = 'string'
// 都是可以的
// 假定这个 nums 来自一个明确的接口
const nums = [110, 120, 119, 112]
const res = nums.find(i => i > 0) // 推导出res 为 number | undefined
// 我们需要告诉Ts,这就是一个number类型
const num1 = res as number
const num1 = <number>res // JSX 下不能使用
一种规范,约定
interface IPost {
title: string;
content: string
}
function printPost(post: IPost) {
console.log(post.title)
console.log(post.content)
}
interface IPost {
title: string;
content: string;
subtitle?: string // 可选参数 string | undefined
readonly summary: string // 不允许修改
}
// 动态成员
interface Cache {
[prop: string]: string
}
用来描述一类具体对象的抽象成员
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
sayHi(msg: string) {
console.log(`I am ${this.name} , ${msg}`)
}
}
protected在子类中使用this是可以访问到的
在访问修饰符后面加上 readonly
// 比如
public readonly name: string;
// 规定协议
interface Eat {
eat(food: string): void
}
interface Run {
run(distance: number): void
}
class Person implements Eat, Run {
eat(food: string): void {
}
run(distance: number): void {
}
}
class Animal implements Eat, Run {
eat(food: string): void {
}
run(distance: number): void {
}
}
抽象类使用 关键字 abstract
export {} // 确保跟其它示例没有成员冲突
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()
定义函数或接口、类的时候没有指定具体的类型,等到我们使用的时候再去指定具体类型的特征
function createArray<T>(length: number, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
const res = createArray<string>(3, 'foo')
为了兼容普通的js模块
import { camelCase } from 'lodash'
declare function camelCase (input: string): string
const res = camelCase('hello typed') // 在这里函数没有提示,需要声明 如上
引用第三方模块,如果模块中不包含类型声明文件,就需要自己声明