(此处存在争议,以下观点仅代表个人理解) 编程语言分为强类型与弱类型,强类型有更强的类型约束,而弱类型中几乎没有什么约束
// 程序类型异常,必须等待执行时才会报错
const obj = {}
obj.foo()
// 两个数组相加,可能会传入非Number参数
funciton sum (a,d){
return a + b
}
sum(100,'abc')
//可以使用任意类型作为属性 属性名会自动通过toString转换
const obj = {}
obj[true] = 100
console.log(obj['true']) // 100
Flow静态类型检查器
2015年由FaceBook提供的一款工具,为JavaScript提供完善的类型检查方案
工作原理
在代码中通过添加类型注解的方式来标记每一个变量或者参数的类型,flow 根据注解解析类型,从而发现类型错误。
安装
# 安装
yarn add flow-bin --dev
# 初始化
yarn flow init
新建一个js文件且在最上部通过注释形式写 //@flow
// @flow
function sum (a:number,b:number){
return a + b
}
sum(1,2)
sum('a','b')
flow编译移除注解–flow-remove-types
# 安装flow-remove-types
yarn add flow-remove-types --dev
# 执行 flow-remove-types
# . 当前目录
# -d 代表 --out-dir
# dist 输出目录
yarn flow-remove-types . -d dist
# 安装 @babel/core babel核心模块
yarn add @babel/core --dev
# 安装 @babel/cli babel命令行工具
yarn add @babel/cli --dev
# 安装 @babel/preset-flow babel Flow注解插件
yarn add @babel/preset-flow --dev
{
"presets" : [
"@babel/preset-flow"
]
}
# 执行
# src 工作目录
# -d --out-dir 输出
# dist 输出文件夹
yarn babel src -d dist
flow + vscode+Flow Language Support 食用更加
flow 类型注解
@flow
// 形参类型注解
function sum (a:number,b:number){
return a + b
}
// 变量类型注解
const num:number = 100
// 返回值类型注解
function foo():number{
return 100
}
// 返回值为空的类型注解
function baz():void{
return 100
}
const a : string = 'string'
const b : number = Infinity // NaN Infinity 100
const c : boolean = true // true false
const d : null = null
const e : void = undefined
const f : symbol = Symbol()
// 数组数据类型限制
const arr1: Array < string > = ['a', 'b', 'c']
const arr2: number[] = [1, 2, 3, 4, ]
// 固定长度的数组也可以成为元组数据类型
const arr3: [String, number] = ['a', 100]
// 对象数据类型限制
const obj: {
foo: string,
bar: number
} = {
foo: 'string',
bar: 100
}
// 可选成员
const obj2: {
foo: string,
bar ? : number
} = {
foo: 'string',
bar: 100
}
// 对象允许添加任意类型 但是 键只可以为 string 值只能是 number
const obj3: {
[string]: [number]
} = {}
obj3.boo = 100
obj3.baz = 200
// 函数数据类型限制
function foo({
num: number
}): number {
return 100
}
// 设置回调函数据类型限制
function foo(callback: (number) => number): number {
return callback()
}
// 特殊数据类型
// 字面量类型
const a: 'foo' = 'foo' //只能存放 foo字符串
// 联合变量
const type: 'success' | 'warning' | 'danger' = 'success' //只能存放 'success'|'warning'|'danger'
const b: string | number = 'string' //100
// 类型别名 or 用 type 声明一个类型
const StringOrNumber = string | number
const c : StringOrNumber = 'string' //100
// mybe类型
const gender : ?number = null
// Mixed 数据类型 强类型 所有类型的集合
function passMixed(value:Mixed){
if(typeof value === 'string'){
value.substr(1)
}
if(typeof value === 'number'){
value * value
}
}
passMixed(100)
passMixed('string')
// any数据类型 弱类型
function passAny(value:Any){
value.substr(1)
value * value
}
passAny(100)
passAny('string')
// 运行环境内置限制
const element: HTMLElement | null = document.getElementById('abc')
TypeScript是JavaScript超集,编译过为JavaScript
# 初始话工程
yarn
# 安装TypeScript
yarn add typescript --dev
const fn = (str: string) => {
console.log(`hello ${str}`)
}
fn('word')
# 编译
yarn tsc 01-test.ts
编译过后可以在当前目录下 看到同名成的.js文件
var fn = function (str) {
console.log("hello " + str);
};
fn('word');
# 生成配置文件
yarn tsc --init
# 执行之后会新增tsconfig.json
{
"compilerOptions": {
"target": "ES5", /* 编译后输出版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* 编译后输出js模块化加载方式: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"outDir": "./dist", /* 输出路径.*/
"rootDir": "./src", /* 源代码所在路径 */
"sourceMap": true, /* 开启源代码映射 */
"strict": true, /* 严格模式 */
"esModuleInterop": true, /* Enables emit interoperability between*、
}
}
├── dist
│ ├── 01-test.js
│ └── 01-test.js.map
├── node_modules
├── package.json
├── src
│ └── 01-test.ts
├── tsconfig.json
└── yarn.lock
tsc
const a: string = 'string'
const b: number = Infinity // 100 NaN
const c: boolean = true
const d: void = undefined
const e: null = null
const f: undefined = undefined
const g: symbol = Symbol()
// 对象类型 支持数组 对象 函数
const foo: object = {} // [] funciton(){}
const obj: { foo: number, bar: string } = { foo: 124, bar: 'string' }
// 数组类型
const arr1: Array<number> = [1, 2, 4]
const arr2: number[] = [1, 2, 4]
// 元组类型
const tupple: [number, string] = [18, 'string']
// 枚举类型
enum PostStatus {
Padding = 0,
Success = 1,
Fail = 2
}
// 如果不赋值那么会自动的从0开始累加
enum PostStatus2 {
Padding,
Success,
Fail
}
console.log(PostStatus2['Success']) // 1
console.log(PostStatus['Success']) // 1
// 函数类型
// ? 代表可选参数
function func1(a:string, b:number,c?:string):string{
return `func1 a=${a},b=${b},c=${c}`
}
console.log(func1('a',1) ) // func1 a=a,b=1,c=undefined
console.log(func1('a',1,'c')) // func1 a=a,b=1,c=c
// 通过 = 给与默认值
function func2(a:string, b:number,c:string='默认值'):string{
return `func1 a=${a},b=${b},c=${c}`
}
console.log(func2('a',1) ) // func1 a=a,b=1,c=默认值
console.log(func2('a',1,'c')) // func1 a=a,b=1,c=c
// 箭头函数方式
const func3 :(a:string, b:number,c?:string)=>string = (a:string, b:number,c:string='默认值')=>{
return `func1 a=${a},b=${b},c=${c}`
}
console.log(func3('a',1) ) // func1 a=a,b=1,c=默认值
console.log(func3('a',1,'c')) // func1 a=a,b=1,c=c
// 使用 ...解构并检查数据类型
function sum(...args: number[]) {
return args.reduce((prev, current) => prev + current, 0)
}
sum(1, 2, 3)
// 任意类型参数 any 属于动态类型 ts不会检查
function stringify(value:any):string{
return JSON.stringify(value)
}
虽然通过隐式类型推断可以简化一部分代码,但是会降低阅读性。所以不建议通过隐式类型推断出变量类型
const age = 18 //推断为 number
const name = 'zzy' //推断为 string
const foo //推断为 any
// ...
const nums = [1, 2, 3, 4, 5]
const res = nums.find(i => i > 0) // 推断为 number | undefined
const num1 = res as number // 断言 res的类型一定为 number
const num2 = <number>res // 断言 res的类型一定为 number(尖括号模式在jsx会有语法冲突)
interface Post {
title:string
content:string
}
function pringPost(post:Post){
console.log(post.title)
console.log(post.content)
}
pringPost({
title:'zzy',
content:'hello word'
})
interface Post {
title:string
content:string
// 可选成员
desc?:string
// 只读属性
readonly sunmary:string
}
const post1:Post = {
title:'Title is post1',
content:'content is post1',
sunmary:'sunmary is post1'
}
// 修改普通属性
post1.title = 'update post1 title'
// 尝试修改只读属性
//post1.sunmary='str' // 报错 Cannot assign to 'sunmary' because it is a read-only property.
interface Cache {
// 可以动态添加成员
[porp:string]:string|number|number[]
}
const obj:Cache = {}
obj.test1='abc'
obj.test2=1234
obj.test3=[1,2,3,4,5]
interface Person{
name:string
age:number
}
interface Student extends Person{
classesId:number
studentId:number
}
const student:Student = {
classesId:1,
studentId:2,
name:'student1',
age:18
}
类的作用描述一类具体事物的抽象特征
class Person {
name:string
age:number
status:boolean=true
constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
console.log(`${this.name} say ${msg}`)
}
}
控制类中的成员可访问级别
class Person {
// public 公共属性
public name:string
// private 私有属性 只允许在当前类中访问的成员
private age:number
// protected 受保护的 允许在子类或当前类中访问的成员
protected status:boolean=true
constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
console.log(`${this.name} status is ${this.status}`)
console.log(`${this.name} age is ${this.age}`)
console.log(`${this.name} say ${msg}`)
}
}
class Student extends Person{
constructor(name:string,age:number){
super(name,age)
console.log(this.name)
//console.log(this.age) // 报错 Property 'age' is private and only accessible within class 'Person'.
console.log(this.status)
}
}
const tom = new Student('tom',18)
private修饰constructor,则constructor只能通过类上的static方法创建
class Person {
public name:string
private age:number
private constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
console.log(`${this.name} age is ${this.age}`)
console.log(`${this.name} say ${msg}`)
}
static create(name:string,age:number):Person{
return new Person(name,age)
}
}
const person = Person.create('tom',18)
person.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody
privateed修饰constructor,则constructor可以通过类上的static方法创建及继承
class Person {
public name:string
private age:number
protected constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
console.log(`${this.name} age is ${this.age}`)
console.log(`${this.name} say ${msg}`)
}
static create(name:string,age:number):Person{
return new Person(name,age)
}
}
const person = Person.create('tom',18)
person.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody
class Student extends Person{
constructor(name:string,age:number){
super(name,age)
console.log(this.name)
}
}
const tom = new Student('tom',18)
tom.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody
class Person {
public name:string
private readonly age:number // readonly 一般跟在修饰符后面
protected constructor (name:string,age:number){
this.name=name
this.age=age
}
sayHi(msg:string):void{
//this.age=20 // 修改只读属性报错 Cannot assign to 'age' because it is a read-only property.
console.log(`${this.name} age is ${this.age}`)
console.log(`${this.name} say ${msg}`)
}
static create(name:string,age:number):Person{
return new Person(name,age)
}
}
const person = Person.create('tom',18)
person.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody
通过接口定义类的成员
interface EatAndRun {
eat(food: string): void
walk(step: number): void
}
class Person implements EatAndRun{
eat(food: string): void {
console.log(`优雅进餐${food}`)
}
walk(step: number): void {
console.log(`直立行走${step}`)
}
}
class Animal implements EatAndRun{
eat(food: string): void {
console.log(`粗鲁进食${food}`)
}
walk(step: number): void {
console.log(`爬行${step}`)
}
}
接口最好的使用或者定义方式:一个接口对应一个能力
interface Eat {
eat(food: string): void
}
interface walk {
walk(step: number): void
}
class Person implements Eat,walk{
eat(food: string): void {
console.log(`优雅进餐${food}`)
}
walk(step: number): void {
console.log(`直立行走${step}`)
}
}
class Animal implements Eat,walk{
eat(food: string): void {
console.log(`粗鲁进食${food}`)
}
walk(step: number): void {
console.log(`爬行${step}`)
}
}
abstract class Animal{
eat(food: string): void {
console.log(`粗鲁进食${food}`)
}
// 定义行走抽象方法
abstract walk (step: number): void
}
class Dog extends Animal{
// 实现行走放大
walk(step: number): void {
console.log(`爬行${step}`)
}
}
const dog = new Dog
console.log(dog.eat('骨头')) // 粗鲁进食骨头
console.log(dog.walk(10)) // 爬行10
定义函数接口或者类的时候不能明确的类型,在使用的时候在指定具体类型
// 只能实现 返回 number 格式的数组
function createNumberArray(len:number,value:number):number[]{
const arr=Array<number>(len).fill(value)
return arr
}
console.log(createNumberArray(3,100)) // [ 100, 100, 100 ]
// 不明确的类型使用 T 来表示 T 会在函数调用时传入
function createArray<T>(len:number,value:T):T[]{
const arr=Array<T>(len).fill(value)
return arr
}
console.log(createArray<number>(3,100)) // [ 100, 100, 100 ]
console.log(createArray<string>(3,'abc')) // [ 'abc', 'abc', 'abc' ]