1.类型安全:强类型 vs 弱类型
强类型:语言层面限制函数的实参类型必须与形参类型相同,不允许任意的隐式类型转换,Python
弱类型:语言层面不会限制实参的类型,允许任意的隐式类型转换,JavaScript
2.类型检查:静态类型 vs 动态类型
静态类型:一个变量声明时它的类型就是明确的,声明过后类型就不允许再修改,Java。
动态类型:运行阶段才能明确变量类型,而且变量的类型也可以随时发生变化,它的变量是没有类型的,变量中存放的值是有类型的,JavaScript。
3.弱类型的问题
君子约定有隐患,强制要求有保障
// ex1: 只有运行到obj.foo()这行代码的时候才会报错
const obj = {}
// obj.foo()
// TypeError: obj.foo is not a function
// ex2: 没有按照约定传入数字,而是传入字符串,就会变成字符串拼接
function sum(a, b) {
return a + b
}
console.log(sum(100, 100)) // 200
console.log(sum(100, '100')) // 100100
// ex3: obj的键会自动转为字符串后再使用
const obj1 = {}
obj1[true] = 100
console.log(obj1[true]) // 100
console.log(obj1['true']) // 100
4.强类型的优势
-
错误更早的暴露
-
代码更智能,编码更准确
-
重构更牢靠
-
减少不必要的类型判断
5.Flow 快速上手
-
安装:
yarn run flow
-
初始化一个config文件:
yarn run flow
-
执行flow命令:
yarn flow
-
示例代码,文件首行必须加注释
@flow
,变量后加冒号和类型的方式,叫类型注解
// @flow
function sum (a: number, b: number) {
return a + b
}
sum(100, 100)
sum('100', '100')
- 执行结果:
6.Flow 编译移除注解
使用node 运行上面的示例,会报错
因为标准的JavaScript语法不识别类型注解(a: number)的写法,所以要自动移除掉类型注解。
解决办法一:
-
安装remove插件:
yarn add flow-remove-types --dev
-
运行remove命令:
yarn flow-remove-types . -d dist
,第一个参数.
表示当前目录下的文件,-d
后的参数,表示输出到dist文件夹下 -
结果
解决办法二:
-
安装 babel:
yarn add @babel/core @babel/cli @babel/preset-flow --dev
-
添加 .babelrc 文件
{
"presets": ["@babel/preset-flow"]
}
-
运行命令
yarn babel .\02-getting-started.js -d dist
-
结果
在vsCode开发工具中可以安装 FLow Language Support 辅助插件
7.Flow 类型推断
8.Flow 类型注解
// @flow
function sum (a: number, b: number) {
return a + b
}
// 变量类型注解
let num: number = 100
num = 'string'
// 返回值类型注解
function add(a, b): number {
return a + b
}
9.Flow 原始类型
/**
* 原始类型
* @flow
*/
const a: string = 100
const b: number = Infinity // NaN // 100
const c: boolean = true // false
const d: null = null
const e: void = undefined
const f: symbol = Symbol()
10.Flow 数组类型
// @flow
const arr1: Array<number> = [1, '1']
const arr2: number[] = [1, '1']
// 固定长度数组(元组)
const foo: [string, number] = ['foo', 100, 10]
11.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['aa'] = 100
12.Flow 函数类型
13.Flow 特殊类型
// @flow
// 字面量类型
const a: 'foo' = 'foo'
a = 'bar'
const type: 'success' | 'warning' | 'danger' = 'success'
const b: string | number = 'string' // 100
type StringOrNumber = string | number
const c: StringOrNumber = 100
// Maybe类型:number|null|void|undefined
const gender: ?number = null
14.Flow Mixed 与 Any
// @flow
// mixed也就是所有类型的联合类型:string|number|boolean|......
// 强类型
function passMixed(value: mixed) {
value.substr(1)
value * value
}
passMixed('string')
passMixed(100)
// any也是所有类型的联合类型:string|number|boolean|......
// 弱类型
function passAny(value: any) {
value.substr(1)
value * value
}
passAny('string')
passAny(100)
mixed报错解决办法
function passMixed2(value: mixed) {
if (typeof value === 'string') {
value.substr(1)
}
if (typeof value === 'number'){
value * value
}
}
15.Flow 类型手册
https://www.saltycrane.com/cheat-sheets/flow-type/latest/
16.TypeScript 快速上手
-
安装:
yarn add typescript --dev
-
运行:
yarn tsc 11-getting-started.ts
-
结果:生成降级的同名文件
修改为类型注解方式,类型不匹配时可以直接提示错误,运行tsc命令也会报错:
17.TypeScript 配置文件
-
初始化配置文件
yarn tsc --init
生成tsconfig.json文件 -
修改tsconfig.json配置文件中的"rootDir": "src",
-
新建src文件夹,在文件夹下修改01-getting-started.ts文件
-
因为配置文件默认开启的严格模式,所以会提示无法推断hello1变量的类型,指定类型
-
修改配置文件tsconfig.json
"sourceMap": true,
"outDir": "dist",
-
运行
yarn tsc
-
结果:
18.TypeScript 原始类型
const a: string = 'foo'
const b: number = 100 // NaN // Infinity
const c: boolean = false // true
// 以上三种类型可以赋值为null, 需要关闭严格模式: "strict": false
const d: string = null
const e: void = undefined // null,严格模式下只能是 undefined
const f: null = null
const g: undefined = undefined
19.TypeScript 作用域问题
定义一个 02-primitive-types.ts 中定义过的变量,就会出现以下报错,因为都是定义在全局作用域中
解决办法:
// 解法一: 放在一个立即执行的函数中
(function () {
const a: number = 100
})()
// 解法二:使用ES Modules
export {}
const a: string = '200'
20.TypeScript Object 类型
export {} // 确保跟其他示例没有成员冲突
// onject:除原始对象外的其他类型
const foo: object = function (){} // [] // {}
const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }
21.TypeScript 数组类型
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)
如果传入的不是参数不是数字,就会报类型错误:
22.TypeScript 数组类型
明确长度和类型的数组
export {}
const tuple: [number, string] = [18, 'bar']
const age = tuple[0] // 18
const [age1, name] = tuple
23.TypeScript 枚举类型
export {}
// status 只能取0 1 2 ,这时就应该使用枚举类型,以前只能使用对象模拟
const PostStatus = {
Draft: 0,
Unpulished: 1,
Published: 2
}
// 现在TypeScript 直接提供了该类型
enum PostStatus1 {
Draft = 0,
Unpulished = 1,
Published = 2
}
// 如果不指定枚举值,默认从0开始
enum PostStatus2 {
Draft,
Unpulished,
Published
}
// 指定了第一项,后面的值在第一项的基础上自增
enum PostStatus2 {
Draft = 6,
Unpulished,
Published
}
// 如果值是字符串,因为没办法增加,只能全部指定值
enum PostStatus3 {
Draft = 'a',
Unpulished = 'b',
Published = 'c'
}
const post = {
title: 'hello TypeScript',
content: 'TypeScript is a typed superset of JavaScript',
// status: PostStatus.Unpulished, // 2 // 1 // 0
status: PostStatus1.Draft
}
编译后,枚举值会以键值对的方式写在代码中,增加代码体积,所以需要enum前加上const
24.TypeScript 函数类型
export {}
function func1(a: number, b: number): string {
return 'func1'
}
func1(100, 200) // 参数个数和类型必须相同
// 可选参数1:参数后加问号,可选参数必须放在最后
function func2(a: number, b?: number): string {
return 'func2'
}
func2(100)
func2(100, 200)
// 可选参数2:参数加默认值,可选参数必须放在最后
function func3(a: number, b: number = 200): string {
return 'func3'
}
func3(100)
func3(100, 200)
// 任意个数参数值
function func4(...rest: number[]): string {
return 'func4'
}
func4(100)
func4(100, 200)
// 函数表达式方式定义, 变量func5可推断出类型
const func5 = function(a: number, b: number): string {
return 'func5'
}
// 如果函数是回调函数,就需要使用箭头函数的方式声明类型
const func6: (a: number, b: number) => string = function (a: number, b: number) {
return 'func6'
}
25.TypeScript 任意类型
export {}
function stringify(value: any) {
return JSON.stringify(value)
}
stringify(100)
stringify('string')
stringify(true)
let foo: any = 'string'
foo = 100
foo.bar()
26.TypeScript 隐式类型推断
export {}
let age = 18 // 推断为number类型
// age = 'string' // 报错
let foo // 无法推断时,就是any类型
foo = 100 // 不会报错
foo = 'string' // 不会报错
27.TypeScript 类型断言
export {}
// 假定nums来自一个明确的接口
const nums = [100, 200, 300, 400]
const res = nums.find(i => i > 0)
const square = res * res
// 断言方式1:推荐
const num1 = res as number
// 断言方式2: JSX 中会冲突
const num2 = <number>res
28.TypeScript 接口
export {}
interface Post {
title: string; // ;可以省略
content: string
subtitle?: string // 可选成员
readonly summary: string // 只读成员
}
function printPost(post: Post){
console.log(post.title)
console.log(post.content)
}
const hello: Post = {
title: 'TypeScript',
content: 'hello world',
summary: 'ts'
}
// hello.summary = '123' // 会报错,只读属性初始化后无法修改
printPost(hello)
interface Cache {
[key: string]: string
}
const cache: Cache = {}
cache['foo'] = 'foo'
29.TypeScript 类的基本使用
描述一类具体事物的抽象特征,用来描述一类具体对象的抽象成员
export {}
class Person {
// 必须先声明属性
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
sayHi(msg: string): void {
console.log(`I am ${this.name}, ${msg}`)
}
}
30.TypeScript 类的访问修饰符
export {}
class Person {
// 必须先声明属性
public name: string
private age: number
protected 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
// console.log(tom.age) // 无法访问
// console.log(tom.gender) // 无法访问
class Student extends Person {
private constructor(name: string, age: number) {
super(name, age)
console.log(this.gender) // 可以访问
}
static create (name: string, age: number) {
return new Student(name, age)
}
}
// const jsck = new Student('jack', 20) 不能new,只能使用静态方法创建
const jack = Student.create('jack', 20)
31.TypeScript 类的只读属性
export {}
class Person {
// 必须先声明属性
public name: string
private age: number
protected readonly gender: boolean // 只读属性
constructor(name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi(msg: string): void {
this.gender = false // 提示只读属性不能修改
console.log(`I am ${this.name}, ${msg}`)
console.log(this.age)
}
}
32.TypeScript 类与接口
export {}
// 建议每个接口实现一种能力
// interface EatAndRun {
// eat(food: string): void
// run(distance: number): void
// }
interface Run {
run(distance: number): void
}
interface Eat {
eat(food: string): void
}
class Person implements Eat, Run {
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}`)
}
}
33.TypeScript 抽象类
与接口不同的是,可以包含实现,接口不能包含实现
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()
d.eat('肉')
d.run(100)
34.TypeScript 泛型
定义的时候不指定类型,调用的时候再指定具体的类型
export {}
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
}
const res = createNumberArray(3, 100) // [100, 100, 100]
// 上面的情况很冗余,用泛型来解决
function createArray<T>(length: number, value: T): T[] {
const arr = Array<T>(length).fill(value)
return arr
}
const res1 = createArray<string>(3, '100')
const res2 = createArray<number>(3, 100)
35.TypeScript 类型声明
使用lodash等插件
-
安装loadsh:
yarn add loadsh
-
引入某个函数:
import { camelCase } from 'lodash'
-
提示没有类型声明:
-
安装声明:
yarn add @types/lodash
-
或者自己针对这个函数编写声明
declare function camelCase(input: string): string